Talk online-Game server design

xiaoxiao2021-03-06  47

Before talking about this topic, you must first let you know what is a server. In online games, the role played by the server is some active behavior of synchronization, broadcast, and server, such as weather, NPC AI, and now many online game servers need to bear some game logic operations because In order to prevent the client from cheating. Understand this, then this series of articles will be divided into two parts to talk about the design of the online game server. Part is how to do the server's network connection, synchronization, broadcast, and NPC settings, the other part will focus Which logic is placed in the server, and uses what kind of structure to arrange these logic. Server network connection Most online games of online games choose non-blocking SELECT, why? Because the server needs to handle the connection, most of them choose to run under Linux / UNIX, then open a thread to each user is actually very disadvantaged, on the one hand, because in Linux / UNIX The thread is such a concept simulation, which compares the system resources. In addition to I / O, each thread basically does not have any eloquent tasks, and online games are very interteted, so Synchronization between threads will become a troublesome problem. From this, it is obviously unrealistic to this single-threaded server containing a large number of network connections. For network connections, you need to store with a structure, where there is a need to contain a buffer to the client's desk, and a buffer from the client read message, the specific size is determined according to the specific message structure. In addition, for synchronization, there is a need for some time proof, and some different values ​​are required to record the current state. The following preliminary connection is given: typedef connection_s {user_t * object; / * Point to process server-side logic * / Int FD; / * Socket connection * / struct socDr_in addr; / * Connected address information * / char text [max_text]; / * received message buffer * / int text_end; / * Receive message buffer tail pointer * / INT text_start; / * Receive message buffer head pointer * / int last_time; / * When is the message received? * / Struct TimeVal latency; / * Client local time and server local time difference * / Struct Timeval Last_confirm_time; / * Last verification time * / short is_confirmed; / * This connection is verified over * / int ping_num; / * The client to the server-side PING value * / int ping_ticker; / * How many IO cycle processing updates PING value * / int message_length; / * Send buffer message length * / char message_buf [max_text]; / * Send buffer * / int iflags; / * The status of the connection * /} connection_t; server loop processing all connections, Is a dead cycle process, each cycle is used to check if there is a new connection to arrive, and then loop all connections, see which connection can be written or read, handle the read and write of the connection. Since all processing is non-blocking, all Socket IO can be done with a thread.

Due to the relationship between network transmission, each recv () to the data may not only contain a message, or if a message is not handled? Therefore, two pointers are buffered for the reception message, each receiving is started from text_start, because the remaining half of the residue may be the superfluous half message received last time, and then text_end points to the end of the message buffer. This can be easily handled by two pointers, and there is another point that is worth noting that the process of parsing the message is a loop process, maybe received two messages in the message buffer, this time There should be only one message in the message buffer, the general process is as follows: while (text_end - text_start> a complete message length) {Start processing from Text_Start; Text_Start = The message length;} Memcpy (Text, TEXT TEXT_START, TEXT_END - TEXT_START); for the process of the message, you will need to know what a total of your game is in total. All messages can design a relatively reasonable message head. In general, the message can be divided into four parts: protagonist messages, scene messages, synchronization messages, and interface messages. Among them, the protagonist messages include all the actions of the role controlled by the client, including walking, running, and fighting. Scene messages include weather changes, some time appear in the scene, etc., this kind of message is characterized by all the initiators of all messages, the broadcast object is all players in the scene. The synchronization message is a player that initiates an object. It is also a player who can see his player through the server broadcast. This message is also a single action, and the protagonist message is that the message is the server broadcast to the client, and The protagonist message is typically a client actively issued a server. Finally, interface messages include chat messages and various attributes and status information that the server sent to the client. Let's talk about the composition of the message. In general, a message consists of two parts of the message head and the message body, wherein the length of the message head is constant, and the length of the message body is variable, and the length of the message body is required in the message body. Since it is a significant distinction to each message, you need to define a message with a message head, and then you need the type of message and the message ID. General header structure is as follows: type struct message_s {unsigned short message_sign; unsigned char message_type; unsigned short message_id unsigned char message_len} message_t; broadcasting server broadcasting server key lies in how the broadcast objects is calculated. Obviously, in a big map, a player is a move that is the most east, a player in the west is not seen, then how can it calculate the broadcast object? The easiest way is to block maps, divided into small pieces of size, and then broadcast only a few small pieces around them each time. So how do you cut how much appropriate? In general, the block is large, the consumption of memory will increase, the block is small, and the consumption of the CPU will increase (whose reason will be mentioned later). Personally think that the small pieces of the left and right are relatively appropriate, and each broadcast broadcast broadcast around nine small pieces of players, because the broadcast is very frequent, the operation around the vibrant will become quite frequent, so if The block is smaller, then the scope of the spread will be expanded, and the resources of the CPU will be eaten quickly.

After cutting the block, how to let the players walk between all blocks? Let us think about what work to do when switching once. First of all, it is necessary to calculate the nine players around the next block, which is not there, and broadcast your information to those players. At the same time, it is necessary to calculate which objects in nine blocks around the next block are not. Broadcasting the information of those objects to yourself, then put the next block in nine high, and now the items of the items in nine in nine items are broadcast to themselves, and they will broadcast their disappeared news to those object. This operation is not only cumbersome and will eat a lot of CPU resources. So what is the way to calculate these items soon? What is more compared? Obviously, it is not a good way. Here you can refer to some ideas for the two-dimensional matrix collision detection. It is nine into a matrix in its own, and nine of the target block is another matrix. It is detected whether these two matrices collide. If two The matrix intersects, how is the blocks that are not intended. Here, the coordinates of the intersecting blocks can be converted into internal coordinates, and then operate. There is another solution for broadcasting, which is not as simple as cut, which requires clients to assist in operation. First, in the server-side connection, you need to add a queue of a broadcast object. The queue passed by the server to the client when the client logs in, and then the client will maintain this queue. When someone goes out of the client's vision, The client actively requires the server to send a disappeared message to that item. And for some people always enter the field of view, it is more troublesome. First, you need to send the client to the server to send Update Position messages, the server calculates a field of view, and then when you need to broadcast, the player on the circulatory map will find the coordinates within its field of view. Player. The advantage of using this method is that when there is no converter, a large number of news is required. The disadvantage is that when calculating the broadcast object, it is necessary to traverse the players on the entire map when calculating the broadcast object. This operation will be slower. The server's synchronization synchronization is very important in the online game, which ensures that every player is generally the same on the screen. In fact, the easiest way to solve synchronization problems is to broadcast every player's movement to other players. In fact, there are two problems: 1. Which players broadcast, which news is broadcast which news. 2. What if the network is delayed. In fact, the first question is a very simple question, but the reason why I put this question is to remind everyone that this factor needs to take this factor when designing your message structure. For the second question, it is a very troublesome problem. You can come to see such examples: For example, there is a player A sent a command to the server, saying that I am in P1, go to P2 point. The time issued by the command is T0. The time of the server receives the command is T1, and then broadcasts the message to the surrounding player. The content of the message is "player A from P1 to P2" has a player B near A. Receive the server The time of this broadcast message is T2 and then starts on the client, A from P1 to P2 points. At this time there is an unacceptable problem, and the screen displayed on the screen of the player A and the player B differs from T2-T1. What should I do at this time? There is a solution, I gave it a prediction to pull, although it is a bit weird, but basically everyone can understand it from the literal. To solve this problem, first define a value called: predict error.

Then you need to add an attribute in the class that each player connected to the server side, called Latency, and then compare the client time and server time when the player is logged in, and the difference is saved in the latency. Or the above example, when the server broadcasts the message, the CURRENTTIME of the client is calculated based on the Latency to broadcast the object, and then contains this CURRENTTIME in the message head, and then broadcast. At the same time, it is also a queue in the client A client to save the message, and only the server authentication will be deleted from the unauthenticated message queue. If the verification fails, it will be pulled back to P1 points. Then when the player b received the message "player a from P1 to P2", check the time and local time to check the server in the server. If it is greater than the prediction error, even if it is in T2, players A location P3 walking on the screen, then pull the player A on the player B directly to P3, then continue to go, so you can guarantee synchronization. Further, in order to ensure that the client runs more Smooth, I don't recommend pulling the players directly, but calculates a point P4 after the P3 is biased, and then uses (P4-P1) / T (P4-P3) to calculate a very Fast speed S, then let the player A quickly move to P4 with speed S, this processing method is reasonable, the original shape of this solution is internationally called (of course, the original shape is tampered with Many to adapt to the synchronization of online games, so becoming a so-called: predict pull. Another solution, I gave it name to verify synchronization, listen to the name, the general meaning is that each instruction has passed the server verification. The specific idea is as follows: First, you need to define a latency in each player connection type, and then at the same time when the client is walking, the client will not walk first, but send a walk to the server, then wait Verification of the server. After the server accepts this message, the logical layer is verified, and then calculates the range of broadcasts, including player A, according to the different news heads according to different Latency, which is different, start broadcasting, this time this player walks Information is completely synchronized. The advantage of this method is to ensure absolute synchronization between the clients. The disadvantage is that when the network delay is relatively large, the player's client's behavior will become less smooth, bringing a very unpleasant feeling to the player. The prototype of this solution is internationally called Hierarchical Master-Slave Synchronization. After the 1980s, it is widely used in various areas of the network. The last solution is an ideal solution that is known as Mutual Synchronization, a good predicted solution for future network prospects. The reason why we have to mention this scheme is not to achieve this approach, but is just some of the ideas of this program in some ways in the field of online games. I named this scheme: half server synchronization. The general design idea is as follows: First, the client needs to establish a lot of broadcast list when landing the world. These lists are not timely synchronization in the client, and to build multiple lists because the type to be broadcast is More than one, for example, there is local message, there is Remote Message, and Global Message, etc., these lists need to be established based on the messages sent by the server when they log in.

At the same time as the list is created, you also need to get the latency of the broadcast object in each list, and maintain a complete user status list in the background, not timely synchronization, according to the local user status table, can do Some decisions are determined by the client. When the client sends this part of the decision, send the final decision to the client inside each broadcast list, and the time is correct, ensuring that each client is received. The time of the message is related to the school according to the local time. Then use the calculated advance amount mentioned in the predictive pulling, improve the speed of the process, will make the synchronous SMOoth. The advantage of this program is not to synchronize between the client, which greatly reduces the error due to network delay, and because most of the decisions can be done by the client, it is also greatly reduced by the server. Resources. The resulting drawback is that the information and decision rights are placed locally locally, so they provide a lot of machines that can be multiplied. Below I want to talk about problems involved in the design of NPC on the server and NPC intelligence. First, we need to know what NPC, NPC needs to do. NPC's full name, it is obvious that he is a Character, but not a player, then you can know, some behavior of NPC is similar to the player, he can walk, you can fight, you can fight, you can Breathing (this will be mentioned in the back NPC intelligence), and the other is different from the player object, NPC can be reborn (ie, NPC is killed in a certain period of time). In fact, there is still the most important point, that is, all the decisions of the player object are made, and the NPC's decision is made by the computer, so when it makes the NPC, it is necessary to make a so-called NPC intelligence. Decision. Below I will talk about two parts to talk about NPC, first is NPC intelligence, followed by organizing NPCs. The reason why NPC intelligence is to be talked to it because only when we understand what we need to do NPC, it starts to design the server to organize NPC. NPC intelligent NPC intelligence is divided into two kinds, one is an event that triggered, one is an event triggered. For passive triggering events, it is relatively simple, and it can be called a function on the NPC by the event itself, such as the death of NPC, in fact, when the NPC's HP is less than a certain value, to actively call the NPC body. Ondie () function, this incident triggered NPC intelligence of NPC behavior, I called passive trigger. This type of trigger is often divided into two: one is the attribute change of NPC caused by other objects, and then attribute changes to NPC generation. From this, at least the following functions are included in the NPC object: Class NPC {public: // Who is where I have changed how much I have changed. OnchangeAttribute (Object_t * Who, Int Which, int how, int where); onescape (); onfollow (); onsleep (); // A series of events. } This is a basic NPC structure, which triggered NPC event, I call it NPC reflection. However, such a structure can only make NPC passive reception of some information to make decisions, such NPC is stupid. So how do you make an NPC make some decisions? Here is a method: breathing.

So how do you let NPC breathe? A simple way, with a timer, timing triggered all NPC breathing, so that an NPC breathes. In this case, there will be a problem. When NPC is too much, the last NPC's breath has not breathed, and the next breath is coming again, then how to solve this problem. Here is a method that allows NPC to breathe asynchronously, that is, each NPC's breathing cycle is based on the time of NPC. This time the timer needs to be done. Which NPCs should breathe when it takes it. To trigger the breath of these NPCs. What mentioned above is how the system triggers the NPC's breathing, then how does the NPC itself set? This is the same as the people in the reality, when sleeping and intense exercise, is not the same. Similarly, when the NPC is fighting, the respiratory frequency is different when he is fighting. Then need a BREATH_TICKER to set the current breathing frequency of the NPC. So how do we set up NPC intelligence in NPC's breathing events? Generally, it can be summarized as two parts of the inspection environment and make decisions. First, you need to count statistics on the current environment. For example, if you fight, there are several enemies, how much your own HP is, and there is no enemies such as enemies. Statarded data into its own decision module, the decision module makes some decisions according to the NPC's own character orientation. For example, the barbaric NPC will still fight when HP is less, and it is more intelligent. NPC will choose to escape when HP is less. The like. At this point, a structure that can breathe, reflecting NPC has been basically constructed, then let's talk about how the system organizes how NPC appears in the world. NPC organizations There are two options here to choose from, and the position information of NPC is saved in the scene, and the NPC is loaded when loading the scene. Second, NPC's location information is saved on NPC, and there is a special event to let all NPC login scenes. What is the difference between these two methods? What is good or bad? The advantage of the former method is that when the scene is loaded, the NPC is loaded, and the scene can manage NPC. It does not require extra processing, and the drawback is that it is simultaneously refreshed in the refresh, which is in the scene. NPC may grow up within the same time. And for the second method, it will be slightly troublesome, need a unified mechanism to let NPC log in to the scene, but also need some troublesome design, but this solution can realize NPC asynchronous refresh, is current online games The method of adopting, let's focus on this method: First of all, we must introduce a "soul" concept, that is, a NPC after death, it disappears is just his body, his soul is still in the world. There is no breathing, floating in the vicinity of death, waiting for time to take the tire, turn the previous attribute when the tire is cleared, and the flesh is constructed on the scene. So, how can we design such a structure? First, the NPC you want to appear in a scene is created, give each NPC a unique identifier, after loading the scene, load NPC belonging to the scene according to the graphic table. In the NPC's onDie () event, the object DESTROY is not directly removed, but the NPC's breathing is turned off, then opens a reborn timer, and finally set the object to Invisable. Such a design can achieve an asynchronous refresh of NPC, while saving server resources while making players feel more real.

(This chapter has involved something related to some server scripts, so the next chapter will talk about some of the designs of server scripts) to talk about heuristic search in NPC intelligence. The main idea is to filter all the nodes of the next layer through a sense function while filtering, and narrows the search within a certain range. The well-known search A * algorithm is the application of typical heuristic search. The principle is to design a Judge (Point_t * Point) function, to get the price of Point, then take the next step by each search. All points have been evaluated by Judge () function, get two to three costs, relatively small points, continue to search, those who have not been selected, will not continue to search, this is possible Not the best path, this is why the A * algorithm will go to the obstacle to go to the obstacle, rather than the oblique line in front of the obstacle. If you want to find the most optimized path, it is not possible to use a * algorithm, but to use a dynamic plan, which consumes a much greater than A *. So, in addition to being outwritten, what places can be applied to a heuristic search? In fact, there is a big bigger, any decisions of NPC can be used in heuristic search, such as escape, if it is a 2D online game, there are eight directions, which direction of NPC to escape? You can set a Judge (int Direction) to give the price of each point, count the strength of the enemy of this point, or how the enemy is agile, etc., and finally choose the minimum place to escape. Below, let's talk about the design of several NPC common intelligent heuristic search methods: Target SELECT: First get a list of enemies near the NPC. Design Judge () functions, according to the strength of the enemy, the enemy's far close, calculate the price. Then choose the minimum enemy for the price to actively attack. Escape: Check your own HP in the breathing event, if the HP is below a certain value, or if you are a remote soldier, and the enemy's approach, trigger the escape function, in the escape function is also surrounded All enemies organize a list, then design the Judge () function, first select the enemy that makes you pose a threat, the Judge () function needs to judge the enemy's speed, the combat power is weak, and finally give a major enemy, then target it The main enemy's function is designed, the search scope can only be the opposite direction and the main enemy, and then calculate the price according to the strength of the enemy in the direction, make the final choice. Random Walk: This is not recommended to use a * algorithm, because NPC has been more, then this consumption of CPU is terrible, and most of NPC does not need long-distance search, only need to walk nearby Go, then, just give a few points nearby, then let NPC go through, if you encounter an obstacle, stop, so there is almost no burden. FOLLOW TARGET: There are two ways here. A method NPC looks more stupid. A method looks more intelligent. The first method is to let NPC follow the goal of the target, there is almost no resources. Consume.

The latter is that when it is followed, it is judged that the other party's current position is judged, and then takes the straight line, and hits the obstacle to use a * to bypass, the design will consume a certain amount of system resources, so Recommend a large number of NPCs, if you need a lot of NPC to follow the target, there is a relatively simple method: let NPC and target synchronize, that is, let their speed uniform, move the same road point, of course, this The design is only suitable for NPC, which is not the relationship of chasing, just follow the players. On this chapter, I want to talk about the design of the script of the server. Because in the previous chapter, talk to some scripts related to the Script of Scripts when talking about NPC intelligence. Or let's talk about the role of the script. In the compiled server-side program, it is impossible to build some things during the running process of the program, then this time you need a support for scripting languages. Since the scripting language involves logical judgment, it is useless to provide some function interfaces. It also needs to provide some simple grammatical and grammar analysis. In fact, any event can be seen as two parts: the first is to change the value of itself, or other objects, and the other is broadcasting the event in a text or graphic. Then, here is a very important topic, which is to address a piece of object. Well, talk about this, I want to divide this section into three parts, first of all, how to manage dynamically created objects (server memory management), how to address a piece of object, third It is the organization and explanation of scripting languages. In fact, the reason why the fourth chapter will talk about the server's memory management is because of the first few chapters, everyone does not have a sense of understanding, may not know what the server's memory management is used. 4.1, Server Memory Management For server memory management, we will adopt a memory pool, also known as static memory management. Its concept is when the server is initialized, the application is a very large memory, called the memory pool, and also applies for a small memory space called the Garbage Recollecting Station. Its general idea is as follows: When the program needs to apply for memory, first check if the garbage collection station is empty. If it is not empty, find a possible memory address from the garbage collection station, and find the corresponding in the memory pool according to the address. Space, assignment to the program, if the garbage collecting station is empty, then apply a memory from the current pointer position of the memory pool; when the program releases the space, give the memory have released the tag, and then put the block The address of the memory is placed in the garbage collection station. The detailed design of the method will be specifically talked. First, we will use a segment-type page system similar to the operating system to manage memory. This benefit is to fully utilize memory pools, and its disadvantage is to manage it. Well, let's take a look at how we define the structure of the pages and segments:

Typedef struct m_segment_s {structure m_segment_s * next; / * Double-line linked list static memory can reach the purpose of random access and order access, how to access it, how to access it. * / Struct m_segment_s * pre; INT flags; // Some of the tags of this segment. INT Start; / / relative to the first address of the page. INT size; // length. Struct m_page_s * my_owner; // I belong to which page. Char * data; // Content pointer. } m_segment_t; typedef struct m_page_s {unsigned int flags; / * uses tags, is it completely used, is there any empty * / int size; / * The size of this page is generally unified, except for the last page * / int end ; / * Wherever use * / int my_index; / * Provides the index * / m_segment_t * segments; // page of the random access. } M_page_t;

So how to build the memory pool and garbage collection station? Some of the pseudo code related to some buildings are also given below:

Static m_page_t * all_pages; // total_size is a total number of memory to apply, NUM_PAGES is a total of how many pages intend to create. Void Initialize_memory_pool (int total_size, int num_pages) {INT I, PAGE_SIZE, LAST_SIZE; / / Calculate the size of each page. Page_size = Total_size / num_pages; // Assign enough pages. All_pages = (m_page_t *) Calloc (num_pages, sizeof (m_page_t *)); for (i = 0; i

/ / Set the size of the last page. IF (Last_Size = Total_Size% Num_pages)! = 0) All_pages [i] .size = last_size;}

Let's take a look at how the garbage collection station is designed:

INT ** GARBAGE_STATION; VOID INT_GARBAGE_STATION (int Num_pages, int page_size) {INT i; garbage_station = (int **) Calloc (num_pages, sizeof (int *)); for (i = 0; i

4.2, the first problem of the address of the object in the game, why should we address? After joining the concept of the scripting language, some of the logical objects in the game, such as NPC, a Item, which is dynamically generated by the scripting language in the process of playing, then what kind of way we pass these The object is indexed? Speaking simple, how do you find them? There is a very simple method, all traversed once. Of course, this is a simple and effective method, but the efficiency consumption is that any server is not allowed, especially after the game is large. So, how can we find these objects in the game world? I want to talk about this, say that Hash Table this data structure, is called a hash table, and some people call it a list, and their working principle is not sequential, nor is it random access, but through a hash function. KEY performs calculation, calculates the address of the value corresponding to this Key in memory, and access it. The advantage is whether it is in the face of how big data, only one calculation can find its address, very fast, what is the shortcomings? When the address calculated by the hash function is the same address, the trouble is coming, it will generate a collision, and its solution is very troublesome. It will not talk about his solution in detail, otherwise it is estimated to write I haven't mentioned it clearly, but if you are interested in it, welcome to discuss. Well, we will use the hash table to index the objects in the game, how do you do it? First, apply for a twice greater than the memory of the total number of objects in the game, why is it twice as large? Prevent the collision of the hash table. Then we use the name of the object as the index key of the hash table, and then you can start design a hash function. Let's take an example:

Static Int T [] = {1, 87, 49, 12, 176, 178, 102, 166, 121, 193, 6, 84, 249, 230, 44, 163, 14, 197, 213, 181, 161, 85 218, 80, 64, 239, 24, 226, 236, 142, 38, 200, 110, 177, 104, 103, 141, 253, 255, 50, 77, 101, 81, 18, 45, 96, 31 , 222, 25, 107, 190, 70, 86, 237, 240, 34, 72, 242, 20, 214, 244, 227, 149, 235, 97, 234, 57, 22, 60, 250, 82, 175 , 208, 5, 127, 199, 111, 62, 135, 248, 174, 169, 211, 58, 66, 154, 106, 195, 245, 171, 17, 187, 182, 179, 0, 243, 132 , 56, 148, 75, 128, 133, 158, 100, 130, 126, 91, 13, 153, 246, 216, 219, 119, 68, 223, 78, 83, 88, 201, 99, 122, 11 , 92, 32, 136, 114, 52, 10, 138, 30, 48, 183, 156, 35, 61, 26, 143, 74, 251, 94, 129, 162, 63, 152, 170, 7, 115 , 167, 241, 206, 3, 150, 55, 59, 151, 220, 90, 53, 23, 131, 125, 173, 15, 238, 79, 95, 89, 16, 105, 137, 225, 224 217, 160, 37, 123, 118, 73, 2, 157, 46, 116, 9, 145, 134, 228, 207, 212, 202, 215, 69, 229, 27, 188, 67, 124, 168 , 252, 42, 4, 29, 108, 21, 247, 19, 205, 39, 203, 233, 40, 186, 147, 198, 192, 155, 33, 164, 191, 98, 204, 165, 180, 117, 76, 140, 36, 210, 172, 41, 54, 159, 8, 185, 232, 113, 196, 231, 47, 146, 120, 51, 65, 28, 144, 254, 221, 93, 189, 194, 139, 112, 43, 71, 109, 184, 209,}; // s is a string pointer that needs to be indexed, and the MaxN is the maximum length of the string, and the return value is the relative address. Inline Int Whashstr (Char * S, INT MAXN) {register unsigned char oh, h; register unsigned char * p; register int i;

IF (! * s) return 0; p = (unsigned char *) s; oh = t [* p]; h = (* (p ) 1) & 0xff; for (i = maxn - 1; * P && --i> = 0;) {oh = t [oh ^ * p]; h = t [h ^ * (p )];} return (oh << 8) h;}

If the specific algorithm does not say, the big paragraph above doesn't ask me why, this algorithm is a algorithm introduced in the CACM 33-6's devil written in Peter K.pears, which is said to be very Fast. With this hash function, we can make very fast addresses through any of the world. 4.3, the scripting language interpretation before designing scripting languages, we first need to understand, what kind of function is our scripting language to implement? Otherwise, you can also write a C's interpreter as you want to do it. The features we have to implement are just simple logical judgments and loops, and all other features can be done by prior function. Well, so we can list a form of work: Design the object to the underlying storage structure, provide access interface between scripts and underlying, design support logical judgment and loop interpreter. Let's talk about the preservation structure of the object in the bottom layer. Specific items to each different attribute, you need to use different structures, of course, if you like, you can use the same structure in all the objects, then design a hash table to save a variety of different properties. But this is not a good way, excessive dependence table will make your game logic and unclear. Therefore, try to distinguish each different object is designed. But one thing is worth noting that no matter what structure, some things are unified, what we say, then how can we design such a piece of object?

Typedef struct object_head_s {char * name; char * prog;} Object_head_t;

Where Name is the index number of this object in the hash table, PROG is the program content that the script interpreter needs to be explained. Below we will design a structure with NPC:

Typedef struct npc_s {object_head_t header; // object head int hp; // NPC HP value. INT level; // NPC level. Struct position_s position; // Current location information. Unsigned int Personality; // NPC personality, a unsigned int 3 can save 24 personality. } NPC_T;

OK, structure design is complete, then how do we design a script interpreter? There are two methods here, one is to parse the script language in the pattern of virtual machines, and the other is designed with similar assembly languages, setting some conditional jumps and loops to implement logical judgment and loop. ,such as:

Set name, "passerby"; choose: random_choose_personality; // Random Select NPC Personality Compare HP, 100; // Comparative qi and blood, the comparison value can be placed in a fixed variable ifness less; // HP <100 If you return it. JUMP choose; / / Otherwise, continue to select, only to one HP <100. Less: Return Success;

转载请注明原文地址:https://www.9cbs.com/read-54978.html

New Post(0)