"Game engine Architecture" reading notes (iv)

Source: Internet
Author: User

A Gameplay system

(1) The game world is generally divided into static elements and dynamic elements, static elements and dynamic elements sometimes do not have a particularly obvious dividing line. But static elements consume less resources and can draw static elements with tools such as brushes.

(2) The game world is generally divided into various blocks, such as levels, maps, regions and so on. Built on top of this is the advanced gameplay process, which is the player's goal, such as a task.

(3) Game dynamic elements are usually designed in an object-oriented manner, often referred to as game objects, entities (entity), actors, or agents.

A) The game object usually extends the language of the engine itself, adding some advanced features such as reflection, and providing access to scripting languages such as Lua.

b) The object model may be divided into the tool-side object model and the runtime object model in some engines, but there must be a connection or even the same implementation.

(4) Data-driven game engine: When the game's behavior can be fully or partially controlled by the data provided by the art and planning, rather than the software written by the programmer, the engine is called data-driven. Data driven reduces the number of iterations and does not need to modify the code of the game itself. But data-driven also has a great price to offer, as well as the ability to provide powerful editing tools and fault tolerance. But anyway, data-driven is also designed to make game development easier, not too blind to complicate, remember the kiss principle.

(5) Game World editor, game World editor including but not limited to the following features:

A) World block creation and management: Levelsystem

b) Visualization of the game world: WYSIWYG editors are best suited for the development of art and planning.

c) Navigation: there must be a flexible camera in the editor, with different modes of rotation around objects and free flight.

d) Selection: Generally, the pickup is carried out in the form of light projection. It is best to support box-select multiple selections and then show them in either a view or a tree diagram.

f) Layers: Support custom groupings in some editors, ability to load a set of layers independently, and arrange different content on different layers.

g) attribute editing: When you select an object in the world of the game, you can adjust the properties of the object through the editor. You can consider adding public property modifications, modifying one, and modifying the corresponding property values for the same type of object.

h) Special object model: such as light source, particle emitter, area, spline and so on in the game world is not visible east, we can give it to add a real bounding box, so that its visible in the world, convenient mobile operations.

i) Read and write the World block: Of course, editing so much, or to save the edited things, for the engine read and write. Some engines use binary files, some engines use text formats such as XML, and some engines store all information in a single file, as well as block blocks.

j) Fast iteration: As we modify the world of the game, the sooner we can see the effect of the change, the better. This feeling CE3 do very well, CE3 editor in the press CTRL + G can immediately enter the game mode, test results. Feel this is really gray often good set!!

K) Integrated Asset management tools: all the resources needed in the game can be browsed at any time, easy to view and edit, and more advanced is the ability to directly make relevant changes in this asset management tool.

Two. Runtime gameplay base System

(1) Runtime object Model schema:

A) Object-centric architecture: Each game object is a single instance of a class, each containing a set of properties and behaviors that are encapsulated in the object.

The simplest of this type of architecture is a single, large class hierarchy, a base class that derives n multiple subclasses down. However, as the number of derived layers becomes deeper, the entire system becomes more difficult to understand maintenance and modification. (Here comes a bubbling effect, which is what the lower class needs, but adds another class that needs this functionality, in order to accommodate this function, which refers to the upper class, resulting in the class becoming larger.) )

Further, you can extend not just the depth of the class, but the derived width. However, this situation is prone to multiple inheritance, which is more difficult to compare.

A better approach would be to use aggregations instead of traditional inheritance methods. Even if you use has a to replace is a. Extract the functionality of some objects as a separate class, and then the game object will have this function or feature as long as the object containing the class is included.

Once the aggregation evolves, it becomes the legendary component model. Various functions and properties belong to one component, while Gameobject is composed of several such components. Each component can be independently maintained for expansion or refactoring without affecting other functions. The simplest is that gameobject contains pointers to all possible components, which can be used to determine which components are needed for the object at initialization time, depending on the parameters or even the configuration file.

The ultimate evolution of components is that only the components do not have GAMEOBJEC, or gameobject contains only one GUID, which also contains GUIDs, but there is gameobject better, if not, the communication between components and other problems will be tricky.

b) Attribute-centric Object model schema:

In this case, the main thing is to store the individual properties of each object, which are stored in the same place, through the object's unique index to obtain this property. The difference between an object-centric schema is that this is the structure of an array, where the object property of the entire game is a struct, and each child of the struct is an array that gets the value from the index of each object. The object-centric case is an array of structures: Each object's property is a struct, and the entire game world is made up of an array of this structure.

(2) data format of World block:

For all kinds of things in the game world, when we edit in the editor, we need to store them and reproduce them in the game engine. There are binary serialization methods, but a better approach is to store only the game state attributes in a text file, such as an XML document.

Because C + + does not have a reflection mechanism, a different class object is created based on the data table for creating objects, such as class factory.

In the editor it is necessary to have the same performance as in the game, and sometimes the interface of the engine is exposed directly to the editor, the benefit is that the editor is exactly the same as in the game world, and it reduces the workload and is more likely to open the way of simulating the game directly in the editor (such as CE3). Sometimes, if you don't need additional functionality, you can also use the generator directly, extracting some of the properties of an object in the engine, and so on, without adding it to the editor.

For properties of various objects it is best to add a default property, and you need to provide a way to modify all object properties at once.

(3) Loading and streaming of the game world:

A) Simple level loading: The general use of the stack allocator, into a closed before, the centralized loading of resources, after use, directly cleared all, and then into a scene (loading interface), and other resources after the completion of loading, continue the game.

b) Barrier room: This is a simple way to avoid loading animations, but it is necessary to match the gameplay. The simplest way is to divide the memory allocated for the resource into two pieces, one for the current scene, and then the next level in the background. And if the player returns to the original scene, it will wear help ... And the barrier room is to deal with this situation. When the player enters the barrier room, the original resources unload, load new resources, let the player in the barrier room do not idle down good.

c) The game world of streaming: this technology bright hanging fried sky ah ...

Streaming needs to be done on the one hand to ensure that the player to play the resources in memory, on the other hand also need to ensure that the resource load can not occur memory fragmentation problem. This requires finer granularity, multiple buffers, and cyclic loading.

Set a bounding box in each block to ensure that the player does not see the help lens.

(4) object generation and memory management:

After the game resource is loaded into memory, you need to manage object generation in the World object. Dynamically allocated memory can be slow, and the size of the game object is different, which can cause memory fragmentation. So memory management is very important.

A) offline memory allocation for objects: Offline is actually an extreme situation where the allocation is a one-time allocation of all memory, and then the unwanted objects remain dormant or invisible. When used, it is activated, simulating the resulting situation. However, this method is more rigid and cannot be used to dynamically generate objects.

b) Dynamic memory allocation: The main problem to deal with is the memory fragmentation problem.

For objects of the same size, the pool allocator can be used. But there are a variety of game objects, so one way is to use a variety of pool allocators to set up a pool allocator for each object. However, this needs to be grasped, to prevent the memory pool of the object is not enough, and some object memory pool remaining.

Another way is to use a similar operating system memory allocation mode, set up a set of memory allocator, from small to large, when the need to allocate, first from the smaller, until the big, if not, directly to the heap of memory, because the bulk of the memory fragmentation problem relatively small pieces of memory fragmentation is not so serious.

One of the most brutal methods is memory relocation, which will be sorted out over time.

(5) Game archive:

A) archive point: The advantage of this is that when the player arrives at the archive point, the game state is determined, so the content of the archive can be relatively small.

b) Any location can be archived: this is more difficult than the previous, the storage state is much more.

But in either case, be careful not to store irrelevant information as much as possible.

(6) World Enquiry:

The game object needs to provide a unique identifier that distinguishes the various objects and can find the object at run time. And the need to find objects is different, such as direct query, search within the scope of the enemy and so on.

A) search for the game object with a unique identifier: Store the game's pointer or handle in a hash table or binary search tree with the game's unique identifier as the health value.

b) Pre-order the game objects and store them in a different list. such as the list of monsters around the player.

c) search for a projection Path collision object: Usually using a collision system.

d) Search for objects within a range of space: You can use some spatial hashing data structures to store game objects, or use legendary four-tree, Yagi-tree, and so on.

(7) Object reference:

A) The simplest is to use pointers, but pointers are also dangerous places, prone to orphaned objects, or null pointer exceptions, invalid pointers and so on, so use caution when using pointers.

b) A more insured approach is to use smart pointers, which generally use reference counting, and add some empty operations. Smart pointer implementation is relatively simple, but to achieve a complete but more complex, strongly do not recommend their own implementation. Smart pointers are also available for us using the boost library.

c) A more advanced approach is to use a handle, the handle is actually an index table, the internal preservation or the game object pointer, but this index table can add more things, and this index table can also be used as an index table of the game world.

(8) Update the game object in real time:

The game runtime needs to update the state of the objects inside the game in real time, which is generally the object's properties. The real-time update, in fact, is still a continuous discrete point in time.

A) The simplest way is to traverse: Each object has a virtual function of update (), which, through polymorphic invocation, updates the state of the game object to the next discrete moment. and passes it a time difference from the previous frame, so that the object can take into account what has been lost and judge what is to be done.

And how to manage the game object is also a problem, the general game has a Gameobjmanager single case, if there is the ability to dynamically generate objects, you need to use a data structure similar to the linked list to manage objects, and if there is no dynamic generation of objects, you can use an array to manage the game object.

With direct traversal, all operations are concentrated in the object's update function, but this is not a good idea.

b) Performance limitations and Batch updates: Update the logical properties of an object, the functions of each subsystem are not updated in the object's update, the object only updates the status values about the subsystem's functionality, and then all objects are updated. Update each subsystem in order.

The benefits of this are: subsystem cache consistency, minimal duplication, reduced resource redistribution, and efficient pipelining.

For updates, there may be interdependencies between objects, which need to be updated in batch order or a set of related objects to be updated together.

But one problem is that it is possible that the state of the object is the state of the previous frame, but the state of the frame is not updated, such as the current state, updated to a, B has not been updated, a needs to look at the state of B, B is the state of the previous frame. A good way to do this is to add a timestamp, which allows the object to query the object's timestamp to determine whether it was a previous frame or a frame.

About the game update also note is not to use the blocking function, to use the non-blocking function whenever possible, put the blocking function to another thread to do.

(1) Event and message pump: The game is driven by events, events that occur during gameplay, and things that you want to focus on. The event-handling system is important, and the main thing is to do two things: the first is to notify the object that is concerned about the event, the so-called message distribution. The second is to arrange for the object to respond to the event of concern, the so-called event handling.

A) The simplest approach is to use function binding: To add a response function for each object to a corresponding event, set a virtual function (or null implementation) in the base class, inherit the virtual function, and then poll each subclass to see if it needs to respond to the message. However, the disadvantage is that the base class needs to have corresponding functions for all types of events, which is very difficult to extend. Furthermore, not all messages need to be polled, so it is not appropriate to do so.

b) A better approach is to encapsulate the event as an object: The event object consists of two parts, one for the event type and the other for the parameter. Some engines call events a command, and in fact, sending an event to an object essentially sends a command to an object.

Wrapping events into classes has many benefits: the first is that an individual is an event handler, and only one virtual function onevent is required to handle all events. Second, a function call like above is instantaneous, and the event is persistent, can be stored in a queue, processed later, or can be used for broadcast. Third, the event can be forwarded, and the forwarded object may not need to know the exact contents of the event.

c) Event type: The simplest way to define an event type is to define the type of event that is required in all games in a single file, and use an enumeration type to map to a unique integer. This one is convenient unified management, the second is relatively simple, the third is the cost of small. But there are many shortcomings, the first, the destruction of encapsulation, second, the event type is hard-coded, not conducive to expansion, third, the index of the enumeration is the order, if a new event type (inserted in the middle), then the order will change, if in the game corresponding to that there is no problem, but, If there are files stored in the corresponding content, the order will be confused. Therefore, the enumeration type uniformly defines the event type, which is good in small programs, but not suitable for large-scale or extended programs.

Another way of event correspondence is a string, which expands very freely, but there are several problems, first, overhead, second, naming conflicts, and third, spelling mistakes. A string hash identifier can be used to resolve performance issues. But naming conflicts requires extra attention. Or add an action that judges the repetition.

D) Event arguments: Event parameters are similar to function parameters, there may be multiple, or different types of arguments.

The simplest way is that the base class wood has parameters, only the type of the message, corresponding to the command without parameters. Other types of messages are used as derived classes for the class, and then the arguments are hardcoded into them.

The second way is to use the Variant collection to store multiple data types. The most troublesome way is to use key-value pairs as parameters to avoid the problem of parameter order.

E) event handler: When a Game object accepts a command, it responds in some way, called event handling. The event handler is usually a native virtual function or script function. Can handle all events, usually containing a series of switch statements.

The event handler needs to take out the arguments provided by the event, the first of which is the function that extracts the parameters by the message packet itself, but this is not good because the base class message packet needs to contain all the message packet extraction virtual functions, in other words, the message packet itself needs to understand the functional structure. The second is that the processing function extracts the message manually and translates it into the desired type, so there may be security issues, but since you know what the message is, there's no problem with it.

Responsibility chain: The game object almost all have to have the dependency, the object relationship in the forwarding event is a common design pattern, called the responsibility chain. Responsibility chain to deal with such a thing: When an event is sent to the chain head, the chain head to determine their own, whether they need to process, and then determine whether the need to forward, you can continue to forward, you can also eat the message packet, no longer forward.

Event forwarding is also used for multicasting, such as when an explosion occurs, and the event needs to be passed to multiple objects within the range. The procedure is to query the objects within the scope and then send the events to those objects.

Registering concerns about events: most objects do not need to accept all events and only focus on a small set of events, so multicasting or broadcasting is inefficient. To register, the simple way is that the event type contains a list of the objects that are concerned with the event, or that each object maintains a group of digits, each of which indicates whether the object is concerned about an event, or if the query is narrowing before the event is sent, querying only the object to which the event is to be followed.

About the event queue: The engine provides the mechanism for immediate processing of the events just emitted, and in addition to the event queue, the event can be cached. This adds flexibility, but also adds complexity. But the personal sense of the event queue is still very useful. The benefits are as follows: First, you can control the timing of event processing, some need to deal with it immediately, and some are not worried, then you can get a slow queue to store these not anxious things, and the order of processing we can also be sorted according to the weights, or insert the time directly in order to insert. Second, even to the future delivery of events, the message packet set a delivery time, can be deferred several frames or a few seconds processing, even can be processed and then send a same packet to achieve a periodic effect. This requires a timestamp to be added to the event package, which is processed only if the game time is greater than or equal to that time. Sort by using a priority queue or when you insert, so that after you have processed the events that you need to handle earlier, you do not need to process them later. The event distribution queue is called at least once per frame and is processed at least once. The priority of events is also important, because time is quantified as frames, then events at the same frame are the same time, and the way to eliminate this ambiguity is to prioritize events to better control the order of event processing.

Of course, the event queue also brings a series of problems. The first is complexity, this is nothing to say. Second, normal we handle an event, you can set a series of parameters when the event is generated, and then pass the object's reference to the event processing system, because there is no scope, after the event is processed, the event itself will be destroyed, but if put in the queue, it involves a problem, function scope ended, The events in the queue are gone, and only a null reference is reserved, so deep replication is required, which involves dynamic memory allocation, and dynamic memory allocation can lead to memory fragmentation, which requires a memory pool allocator. (But I was thinking, can you use value-passing directly in the event-handling queue instead of using references?) Although there is a cost problem, but the event is directly in the queue, not with the previous set up an object outside the same, but also save the trouble of managing memory. Thirdly, the debugging is difficult, the sending and processing of events is not together, not even in a frame, this certainly debugging up a lot of trouble.

The problem with the non-queued event handling system is that it can cause stack calls to be very deep and even stack overflows.

In short, the event-handling system is very flexible, much more flexible than the direct use of C + + Object Association, but the above is not flexible enough, more powerful is the real use of data-driven event delivery system, do not use hard coding, in the editor to the object, provide a variety of options, Enable it to respond to different events. One approach is to provide a scripting language interface, you can define different event types, provide script callbacks, and so on, a more bullish way is to provide a flowchart, link flowchart to define the object flow. This flowchart, an event may correspond to many response objects, but the event itself may not know, one way is to remove the event type, provide an input port to the object, and then the event connected to the port, if a particular message is entered in the port, then the related processing operation.

f) Script:

The script allows the engine user to do simple programming to control the engine, such as adding a mod, is a convenient way.

There are two types of scripts, one defining data and the other for reading data, which is more flexible than hard coding. And the other is the real script, can control the flow of the program, the script can be executed in the game process.

Scripting language is generally interpreted language, by a virtual machine to explain the scripting language, this virtual machine is generally lighter, support fast iteration, do not need to recompile the program, immediately after the modification can see the effect, and relatively easy to use.

The schema required by the script:

1. Callback script: In this architecture, the main functions of the engine are mostly hard-coded by the native programming language, only the key parts of the small features are designed to be customizable, these parts are generally designed as callback scripts, that is, scripts written out of functions, called by the program. For example, when updating an object, write some optional features that use a script callback. This callback function can be written in native language, script control is not used, and can be directly callback with scripting language.

2. Event handler script: It is also a special hook function that allows the game object to respond to events occurring in the game world, or to respond to events in the engine itself.

3. Expand the Game object type with a script or generate a new type: Bind the script to the game object and control the game object type based on inheritance or aggregation.

4. Component or attribute scripting: In the component model-based object model, you need to use a script to determine which components the object requires, so that you have flexible control over the type and properties of the generated object.

5. Script-Driven engine system: scripts can be used to drive the operation of the entire engine, such as the object model is completely scripted, and only use the native engine code when some underlying functionality is required.

6. Script-driven game: native code only as a library, script code is the main body of the game run.

Features required by the script:

1. Invocation with native language: scripting language calls native language, native language can also invoke scripting language, so it is very flexible to control the game.

2. The handle of the game object: Since the scripting language cannot manipulate pointers in the native language directly, how to get a game object needs to be considered, usually using a handle to refer to the game object. Handle, mentioned before, one is to use a numeric GUID, although the space is small, but not intuitive, such as to directly use a script to manipulate an object, can not find the GUID is more troublesome. The other is a string handle, which is straightforward, and if you want to do something with a script, you can manipulate the object directly using the object name. But this has the performance cost, the string must be much slower, and if someone modifies the object name in the editor without modifying the name in the script, there is a possibility of a problem. Another way to compromise is to use a string hash identifier to read as a string, and to have the performance of a run-time integer.

3. Receiving and Handling events: Event processing is one of the core functions of the engine, and if you give the event processing to the script, you will be able to control the process of the game more flexibly. Events are usually related to an object, and some engines use a class of objects to bind a type of script, but this is flexible, such as a class of objects with the same script, and then each object can bind a specific script, providing a variety of scripts as components for invocation. The script is, of course, more than just binding on an object, binding on the engine itself to handle the engine's events, or being bound to an area, or as a standalone trigger, and so on. More flexible is that, using the state machine idea, the script is divided into different states, for different operations. Or when there are events, you can consider native language processing, you can also choose to use the script, through the state machine to judge, if this state, execute the script, otherwise ignore the script.

4. Send event: The engine Tuning script processing has been very flexible, then the script in turn calls the engine is undoubtedly more flexible. The script can pass the engine-defined message to the engine and then the engine processes it. Even so, the script completely defines the new event type, and the script of the other object defines the method that accepts the event type, so the control between the scripts is fully implemented and the flexibility is higher.

The last one is a high-level script, that is, the high-level does not count, not control the engine, but the script to control the task system, the system is usually implemented as a state machine, each state represents the player's task target, this script controls the game in the event of failure or when the task is achieved. The higher level is the parallel task system, this can not be implemented with a simple state machine.

Three. Other content:

In the final chapter, this chapter focuses on some of the other parts of the engine that are needed, although not as important but essential parts as above:

A) audio system: The fourth dimension of the game world

b) Video interface: CG Animation bar DA

c) network interface: Online games spicy fire, of course, it is necessary

d) Player mechanism: The game is a collection of many objects, but we control the character is the most important, but also the core of the part.

f) Camera: The camera is our view of the world of the game window, its importance is conceivable. The game usually has these kinds of cameras:

1. Watch the camera: Rotate around an object and pull it closer

2. Follow the camera: commonly used in platform games, third-person games, racing games, focusing on the player itself, the need to pay attention to the camera itself collision, do not appear through the object's wearing lens, but also to the player must adjust the flexibility of the camera operation.

3. First person camera: this is necessary for the first person game, fixed in the role of the eye position, you can control the direction of the camera through the mouse.

4. Real-time strategy camera: The legendary god view, can simulate the camera floating on the terrain, angle down, the player can control the camera to move horizontally on the terrain, but can not control the camera yaw angle and pitch angle.

5. Movie Camera: Track view, such as when playing the plot, the effect of the mirror.

g) Artificial Intelligence: This is also a big head in the game. Ai mainly includes: pathfinding, perceptual system, line of sight system, environmental understanding, memory function and so on.

h) In fact, the game world far more than these, I feel, the game is a world, the reality of some, the game can have, game development never-ending!

Spent 1.5 months, finally read this book, feel really is a good book, let me really know what the game engine contains what content, feel the game is really unfathomable, really sincerely admire those who write the engine of the Daniel.

Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.

"Game engine Architecture" reading notes (iv)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.