Multi-Threaded Game Engine Design phần 10 ppsx

53 281 0
Multi-Threaded Game Engine Design phần 10 ppsx

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

//coord-sprite collision if (spriteCollision) { Vector2 pos = objectPos.ToD3DXVECTOR2(); font->Print( (int)pos.x, (int)pos.y, objectName); } //draw cursor cursor->setPosition(mouse); cursor->RenderFast(); } //helper function for game_event void sprite_update(Sprite* sprite) { if (sprite->getName() == "FATSHIP") { //rotate the sprite float angle = (float) sprite->getRotation(); sprite->setRotation( angle + 0.005 ); } spriteCollision = 0; Rect b = sprite->getBounds(); if (intersectsCoordsToRect(b,mouse)) { spriteCollision = 1; objectName = sprite->getName(); objectPos = sprite->getPosition().ToD3DXVECTOR3(); } os ( "Sprite Collision: " ( spriteCollision ( endl; } //helper function for game_event void mesh_update(Mesh* mesh) { string name = mesh->getName(); if (name == "OILDRUM" || name == "CRATE") { //rotate the mesh Vector3 rot = mesh->getRotation(); rot.z += 0.1; mesh->setRotation(rot); } os.str(""); os ( "Mouse " ( mouse.x ( "," ( mouse.y ( endl; //create a ray based on mouse coords, test for collision if (!hasHit) { hitDistance = (int) intersectsCoordsToMesh(mesh, mouse); 520 Chapter 17 n Picking and Collision Detection Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com hasHit = (hitDistance != -1.0f); objectName = mesh->getName(); objectPos = mesh->getPosition(); } os ( "Mesh Intersection: " ( hasHit; if (hasHit) { os ( ", distance: " ( hitDistance; os ( ", hits: " ( hits; } os ( endl; } void game_event(Octane::IEvent* e) { switch (e->getID()) { case EVENT_ENTITYUPDATE: { EntityUpdateEvent* evt = (EntityUpdateEvent*) e; switch(evt->entity->getEntityType()) { case ENTITY_SPRITE: { Sprite* sprite = (Sprite*) evt->entity; sprite_update(sprite); } break; case ENTITY_MESH: { Mesh* mesh = (Mesh*) evt->entity; mesh_update(mesh); } break; } } break; case EVENT_ENTITYRENDER: EntityRenderEvent* evt = (EntityRenderEvent*) e; break; case EVENT_KEYRELEASE: { KeyReleaseEvent* evt = (KeyReleaseEvent*) e; switch (evt->keycode) { case DIK_ESCAPE: g_engine->Shutdown(); break; } } Picking 521 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com break; case EVENT_TIMER: TimerEvent* t = (TimerEvent*) e; break; case EVENT_MOUSEMOVE: { MouseMoveEvent* evt = (MouseMoveEvent*) e; mouse.x = evt->posx; mouse.y = evt->posy; } break; } } Collision Detection The two types of collision testing we will utilize are bounding rectangle and distance (or bounding circle). If you know the location of two sprites and you know the width and height of each, then it is possible to determine whether the two sprites are intersecting. Likewise, if you know the position and size of two meshes, you can determine whether they are intersecting as well. Bounding rectangle collision detection describes the use of a sprite’s image or animation frame boundary for collision testing. You can get the upper-left corner of a sprite by merely looking at its X and Y values. To get the lower-right corner, add the width and height to the position. Collectively, these values may be represented as left, top, right, and bottom of a rectangle. Automated Collision Detection The game engine should be capable of calculating and reporting collision events automatically using its entity list. What we want the engine to do is automati- cally perform collision detection, but then notify the game when a collision occurs in a pull or polled manner. We could fire off an event when a collision occurs, but collisions are highly dependent on the gameplay—we simply do not need to test for collisions among all entities, since that does not reflect realistic gameplay. For instance, it’s a waste of processing to test for collisions between the player’s ship and its own missiles, while we do want to test for collisions between those same missiles and enemy ships. So, instead of firing off an event, we’ll set a flag within each entity (collided) and reset the flags every frame. The flag approach also has the added performance benefit of allowing us to skip any entities that already have the flag set. 522 Chapter 17 n Picking and Collision Detection Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com First, we need a new global collision property in the engine so that it is possible to globally enable or disable collisions (for game states or conditions where we do not want collision to take place, possibly for performance reasons). bool p_globalCollision; void enableGlobalCollisions(){ p_globalCollision = true; } void disableGlobalCollisions(){ p_globalCollision = false; } void setGlobalCollisions(bool value) { p_globalCollision = value; } bool getGlobalCollisions() { return p_globalCollision; } We want support for collision testing for sprite-to-sprite and sprite-to-mesh via these new engine functions. The Entity-to-Entity function is called from the main Engine::testForCollisions() function and selectively calls one of the other two based on the EntityType property of each entity. bool Collision(Entity* entity1, Entity* entity2); bool Collision(Sprite* sprite1, Sprite* sprite2); bool Collision(Sprite* sprite, Mesh* mesh); Advice The collidable property for entities is set to false by default. When creating a new managed entity, be sure to manually enable its collision property. These three overload functions should be expanded if new entity types are added to the entity manager. The first Collision() function (with Entity parameters) will call on the other three to perform specific collision tests between the entity types. Testing for a collision or intersection between a sprite and a mesh calls for a special technique called ray casting. What we need to do is calculate the sprite’s position in 3D space (based on our camera’s projection and view matrices) and cast a ray in the direction of that position on the screen parallel to the camera’s orientation, and then see if the ray intersects with any geometry in the scene at that location. In order to perform sprite-to-mesh collision testing, we have to make use of the “picking” function developed earlier, called intersectsCoordsToMesh(). But that leads to a problem: this function requires the projection and view matrices, and those are found in the Camera class, which has nothing at all to do with collision testing, and is a gameplay object not managed in the engine. We have to come up with a rather ugly workaround, unfortunately, but one that will be easy to Collision Detection 523 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com use. In the Mesh class, which is therefore available to BoneMesh as well, is a pair of new properties to handle the projection and view matrices: p_collision_proj and p_collision_view. There are helper functions to assist: * void setCollisionMatrices(Matrix proj, Matrix view) * Matrix getCollisionProjMatrix() * Matrix getCollisionViewMatrix() If the camera does not move, then it’s easy enough to call Mesh::setCollision- Matrices() when creating the new Mesh object. But if the camera changes its position or target then this function will need to be called again while the game is running. With these new properties available, then sprite-to-mesh collision testing can be done with a newly modified version of intersectsCoordsToMesh(), which is now integrated into the Engine class. float Engine::intersectsCoordsToMesh(Mesh* mesh, Vector2 vec) { D3DXMATRIX projection = mesh->getCollisionProjMatrix(); D3DXMATRIX view = mesh->getCollisionViewMatrix(); //convert coords to projection space Vector3 ray; int w = g_engine->getScreenWidth(); int h = g_engine->getScreenHeight(); ray.x = (((2.0f * vec.x) / w ) - 1) / projection._11; ray.y = -(((2.0f * vec.y) / h) - 1) / projection._22; ray.z = 1.0f; //transform screen space pick ray into 3D space D3DXMATRIX m; D3DXMatrixInverse( &m, NULL, &view ); D3DXVECTOR3 rayOrigin,rayDir; rayDir.x = (float) (ray.x*m._11 + ray.y*m._21 + ray.z*m._31); rayDir.y = (float) (ray.x*m._12 + ray.y*m._22 + ray.z*m._32); rayDir.z = (float) (ray.x*m._13 + ray.y*m._23 + ray.z*m._33); rayOrigin.x = m._41; rayOrigin.y = m._42; rayOrigin.z = m._43; //create normalized ray D3DXMATRIX matInverse; D3DXMatrixInverse(&matInverse,NULL,&mesh->getMatrix()); D3DXVECTOR3 rayObjOrigin,rayObjDir; D3DXVec3TransformCoord(&rayObjOrigin,&rayOrigin,&matInverse); 524 Chapter 17 n Picking and Collision Detection Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com D3DXVec3TransformNormal(&rayObjDir,&rayDir,&matInverse); D3DXVec3Normalize(&rayObjDir,&rayObjDir); //ray-mesh intersection test int hasHit; float distanceToCollision; D3DXIntersect( mesh->getMesh(), &rayObjOrigin, &rayObjDir, &hasHit, NULL, NULL, NULL, &distanceToCollision, NULL, NULL ); if (hasHit) return distanceToCollision; else return -1.0f; } Advice Although we have an opportunity to support collision with other types of objects, the code here is written specifically for Sprite and Mesh classes (and through inheritance, BoneMesh as well). If you want to support collision detection with other types of objects (for instance, VectorShape), you can duplicate this code and adapt them to subclass Entity in a similar manner. The testForCollisions() function goes through the entities and performs several conditional tests before actually calling on the collision support function to perform a collision test. First, the RenderType of the entity is tested because we are currently only concerned with collisions between like objects. When the entity has been verified to be collidable—its alive and collidable properties are true— then it becomes the focus of attention for collision testing. For every other like object in the list, the same set of comparisons is made. void Engine::testForCollisions() { //reset all collided properties BOOST_FOREACH( Entity* entity, p_entities ) entity->setCollided(false); //escape if global collisions are disabled if (!p_globalCollision) return; BOOST_FOREACH( Entity* first, p_entities ) { if (first->getAlive() && first->isCollidable() && !f irst->isCollided()) { //test all other entities for collision BOOST_FOREACH(Entity* second, p_entities) { //do not test object with itself if (second->getID() != first->getID()) { Collision Detection 525 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com if (second->getAlive() && second->isCollidable() && !second->isCollided()) { //test for collision if (Collision( first, second )) { //set collision flags first->setCollided(true); second->setCollided(true); } }//if }//if }//foreach }//if }//foreach } Now let’s check out the collision methods that do all the real work of performing a collision test. bool Engine::Collision(Entity* entity1, Entity* entity2) { switch (entity1->getEntityType()) { case ENTITY_SPRITE: switch (entity2->getEntityType()) { case ENTITY_SPRITE: //sprite-to-sprite return Collision((Sprite*)entity1, (Sprite*)entity2); break; case ENTITY_MESH: //sprite-to-mesh return Collision((Sprite*)entity1, (Mesh*)entity2); break; } break; case ENTITY_MESH: switch (entity2->getEntityType()) { case ENTITY_SPRITE: //sprite-to-mesh return Collision((Sprite*)entity2, (Mesh*)entity1); break; } break; } 526 Chapter 17 n Picking and Collision Detection Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com return false; } bool Engine::Collision(Sprite* sprite1, Sprite* sprite2) { Rect r1 = sprite1->getBounds(); Rect r2 = sprite2->getBounds(); if (r1.Intersects(r2)) return true; else return false; } bool Engine::Collision(Sprite* sprite, Mesh* mesh) { //get sprite position Vector2 pos = sprite->getPosition(); //adjust for sprite center pos.x += sprite->getWidth()/2; pos.y += sprite->getHeight()/2; //test for ray-to-mesh intersection float dist = intersectsCoordsToMesh(mesh, pos); if (dist > -1.0) return true; else return false; } Bounding rectangle collision testing makes use of the Rect class (introduced in Chapter 14). While we could have expanded the existing RECT struct, the problem with RECT is that it uses integers while we need floating-point precision. Refer back to Chapter 14 for the sources for the Rect class. The Collision Demo We will put the new automated collision detection features to the test with a program called Collision Demo, included with this chapter’s resource files. In Figure 17.4, you can see the result of a sprite-to-sprite collision reported (see message at upper left). The mouse is actually in control of the large “lightning ball” sprite, which you can use to move on the screen to test for collisions with an example sprite and example mesh. The next screenshot shown in Figure 17.5 shows the collision report when the mouse cursor sprite is moved over the mesh. Only the most relevant portions of this program are included in the code listing—refer to the complete project for the complete source listing with comments and error handling intact. Font *font = NULL; Camera* camera = NULL; Effect* effect = NULL; Collision Detection 527 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Vector2 mouse; ostringstream os; string collisionMessage=""; bool game_init(HWND window) { g_engine->hideCursor(); font = new Font("Arial Bold",18); camera = new Camera(); camera->setPosition(0,0,50); camera->Update(); effect = new Effect(); effect->Load("ambient.fx"); effect->setTechnique("Ambient"); Figure 17.4 The engine now supports automatic sprite-to-sprite collision detection. 528 Chapter 17 n Picking and Collision Detection Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com effect->setViewMatrix( camera->getViewMatrix() ); effect->setProjectionMatrix( camera->getProjMatrix() ); //add a managed mesh { Mesh* mesh = new Mesh(); mesh->setName("CRATE"); mesh->Load("crate.x"); mesh->setScale(0.15); mesh->setPosition(-10,10,0); mesh->setRotation( rand()%360, rand()%360, rand()%360 ); mesh->setEffect( effect ); mesh->setCollidable(true); Figure 17.5 The engine also supports automatic sprite-to-mesh collision detection. Collision Detection 529 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com [...]... (double)((rand() % 10) / 100 ); vy = (double)((rand() % 10) / 100 ); vz = (double)((rand() % 10) / 100 ); radius = (float)(rand()%20)+1; angle = (float)(rand()%360); angVel = (float)(rand()%5) / radius + 0.1f; scale = Vector3(1.0f, 1.0f, 1.0f); rotation = Vector3(0.0f, 0.0f, 0.0f); } void Object::Update() { //see if object has gone too far out of bounds if (position.x < -100 000 || position.x > 100 000) position.x... of the experimental program with 100 0 objects with various face counts (which determines the quality of the sphere used in the demo) Table 18.2 shows similar data for 5000 objects with various sphere quality values Note that results will vary widely from one system to the next Table 18.1 OpenMP Experiment Results: 100 0 Objects Objects Faces 100 0 100 0 100 0 100 0 100 0 100 0 24 112 480 1984 4512 8064 Threads... 100 000) position.x *= -1; if (position.y < -100 000 || position.y > 100 000) position.y *= -1; if (position.z < -100 000 || position.z > 100 000) position.z *= -1; //copy mass values into mesh scaling scale.x = mass * SCALE_MULTIPLIER; if (scale.x > 2.0) scale.x = 2.0; 542 Simpo 18 Chapter PDFnMerge and Split Unregistered Version - http://www.simpopdf.com Threading the Engine scale.z = scale.y = scale.x; //slight... cursor->setName("CURSOR"); cursor->setCollidable(true); g _engine- >addEntity( cursor ); } return true; } void game_ render2d() { os.str(""); os ( "Core: " ( g _engine- >getCoreFrameRate(); os ( ", Screen: "(g _engine- >getScreenFrameRate()(endl; os ( collisionMessage ( endl; font->Print(0,0, os.str() ); collisionMessage = ""; } //helper function for game_ event void sprite_update(Sprite* sprite) { string name... updating code separately from rendering code, which run at different speeds, and such an over-arching approach to threading the engine would prevent us from synchronizing objects that need to interact in a game However, we can thread the Engine: :Update() function and others in the Engine class! The performance of the two or three experiments we’ll be conducting is not as important as the comparisons that... comparison to other engines or demos, which will be using totally different techniques Advice If you are sensitive to the performance of this code and want to see how much you can improve it, be sure to set your DirectX runtime to Retail mode rather than Debug mode using the DirectX Control Panel The first thing we need to do is experiment within the gameplay side of the engine that is, in our engine consumer... that is, in our engine consumer project, and our main.cpp to be specific The first experiment with OpenMP will use the existing engine as a renderer and event manager while all entities will be managed outside the engine in our gameplay code The second experiment will use the engine s entity manager and OpenMP internally Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com OpenMP... project (including a copy of the engine) is found in the “OpenMP External” folder in the book’s resource files “External” refers to the fact that this project does not have any threading code embedded in the engine it’s all in the gameplay project (main.cpp) The “OpenMP Internal” project in the chapter’s resource files is the version with OpenMP integrated into the engine Figure 18.1 shows the first... for our game engine As with any solution to a programming problem, there are alternatives, and even better ways of doing things As we discussed in this chapter, there are ways to optimize collision algorithms You should consider optimizing the collision system to work best with the type of game you’re building at any particular time, as the code presented here is meant to be a foundation for a gameplay... threading technology studied back in Part I to the game engine developed in the chapters of Part II The rudimentary threading techniques will be tested first before we explore more complex threading code, such as running engine modules in separate threads The goal is to explore optimization techniques, including threading n Chapter 18: Threading the Engine 535 Simpo PDF Merge and Split Unregistered . ); cursor->setName("CURSOR"); cursor->setCollidable(true); g _engine- >addEntity( cursor ); } return true; } void game_ render2d() { os.str(""); os ( "Core: " ( g _engine- >getCoreFrameRate(); os ( ", Screen: "(g _engine- >getScreenFrameRate()(endl; os. to threading the engine would prevent us from synchro- nizing objects that need to interact in a game. However, we can thread the Engine: :Update() function and others in the Engine class! The. experiment within the gameplay side of the engine that is, in o ur engine consumer project, and our main.cpp to be specific. The first experiment with OpenMP will use the existing engine as a renderer

Ngày đăng: 13/08/2014, 22:21

Mục lục

  • PART I: AN INTRODUCTION TO SYMMETRIC MULTI-PROCESSING

    • Chapter 1 Overview of Symmetric Multi-processing Technologies

      • Digging In to SMP

      • Overview of Multi-threading Technology

      • Chapter 2 Working with Boost Threads

        • Punishing a Single Core

        • Spreading Out the Workload

        • Chapter 3 Working with OpenMP

          • Say Hello To OpenMP

          • What Is OpenMP and How Does It Work?

          • Chapter 4 Working with POSIX Threads

            • Introducing the POSIX Threads Library

            • Chapter 5 Working with Windows Threads

              • Exploring Windows Threads

              • PART II: CREATING AN ENGINE FOR SMP EXPERIMENTATION

                • Chapter 6 Engine Startup

                  • Why Build an Engine Yourself?

                  • Creating the Engine Project

                  • Verifying Framerates with FRAPS

                  • Chapter 7 Vectors and Matrices

                    • Vectors and Points

                    • Chapter 8 Rendering the Scene

                      • The Camera (View and Projection Matrices)

                      • The Scene (World Matrix)

                      • Chapter 9 Mesh Loading and Rendering

                        • Mesh Loading and Rendering

                        • Chapter 10 Advanced Lighting Effects

                          • Textured Point Light Shader

                          • Chapter 11 Wrapping the Sky in a Box

                            • Building a Skybox

                            • Chapter 12 Environmental Concerns: Recycling Terrain Polygons

                              • Outer Space Environments

                              • Chapter 13 Skeletal Mesh Animation

                                • Hierarchical Mesh Structure

                                • Loading a Skeletal Mesh

Tài liệu cùng người dùng

Tài liệu liên quan