00001 // /////////////////////////////////////////////////////////////////////////// 00002 // lesson06_main.cpp by Victor Dods, created 2006/10/01 00003 // /////////////////////////////////////////////////////////////////////////// 00004 // Unless a different license was explicitly granted in writing by the 00005 // copyright holder (Victor Dods), this software is freely distributable under 00006 // the terms of the GNU General Public License, version 2. Any works deriving 00007 // from this work must also be released under the GNU GPL. See the included 00008 // file LICENSE for details. 00009 // /////////////////////////////////////////////////////////////////////////// 00010 00011 00012 // /////////////////////////////////////////////////////////////////////////// 00013 // Lesson 06 - PhysicsHandler And Additional Object Layers 00014 // /////////////////////////////////////////////////////////////////////////// 00015 00016 /* @endcode 00019 This lesson will 00020 00021 <ul> 00022 <li>@ref lesson06_main.cpp "This lesson's source code"</li> 00023 <li>@ref lessons "Main lesson index"</li> 00024 </ul> 00025 00026 In this lesson, we will build upon the @ref lesson05 "previous lesson" by 00027 moving our physics code into an implementation of the pure virtual class 00028 @ref Xrb::Engine2::PhysicsHandler. 00029 00030 Except for the very simplest cases (such as in this lesson), physics code gets 00031 very complicated. Thus, through PhysicsHandler, it is separated out of the 00032 World class. Another possibly more important reason for this separation is to 00033 provide an interface class by which games can implement their own 00034 application-specific physics behavior. This leaves the World implementation 00035 to handle things like game flow/logic. 00036 00037 We will only slightly build upon the previous lesson. We will write our own 00038 custom implementation of PhysicsHandler, into which we will transplant the 00039 physics code from the previous lesson. We will also make a simple addition 00040 of a background ObjectLayer which will contain a starfield, to demonstrate 00041 the parallaxing effect of WorldView. 00042 00043 <strong>Procedural Overview</strong> -- Items in bold are additions/changes to the previous lesson. 00044 00045 <ul> 00046 <li>Global declarations</li> 00047 <ul> 00048 <li>Declare subclass of Engine2::Entity specific to this app.</li> 00049 <li><strong>Declare subclass of Engine2::PhysicsHandler specific to this app.</strong></li> 00050 <ul> 00051 <li><strong>Implement the pure virtual methods required by the 00052 PhysicsHandler interface class. These are AddObjectLayer, 00053 SetMainObjectLayer, AddEntity and RemoveEntity.</strong></li> 00054 <li><strong>Override HandleFrame to do once-per-frame gravitational 00055 simulation calculations and to update the velocities and positions 00056 of the dynamic objects.</strong></li> 00057 </ul> 00058 <li>Declare subclass of Engine2::World specific to this app.</li> 00059 <ul> 00060 <li><strong>The constructor will instantiate the application-specific 00061 PhysicsHandler and pass it to the superclass Engine2::World 00062 constructor.</strong></li> 00063 </ul> 00064 <li>Declare subclass of Engine2::WorldView specific to this app.</li> 00065 </ul> 00066 <li>Main function</li> 00067 <ul> 00068 <li>Initialize the Pal and game engine singletons. Create the Screen object.</li> 00069 <li>Execute game-specific code.</li> 00070 <ul> 00071 <li>Create application-specific objects and GUI elements, and make necessary signals.</li> 00072 <ul> 00073 <li>Create the game world via CreateAndPopulateWorld.</li> 00074 <li>Create the WorldViewWidget and set it as screen's main widget.</li> 00075 <li>Create the game-specific WorldView.</li> 00076 <li>Attach the WorldView to the WorldViewWidget.</li> 00077 <li>Attach the WorldView to the World.</li> 00078 </ul> 00079 <li>Run the game loop</li> 00080 <ul> 00081 <li>Calculate the Singleton::Pal().Sleep duration necessary to achieve the desired framerate.</li> 00082 <li>Handle events (user and system-generated).</li> 00083 <li>Perform off-screen processing, including game world processing.</li> 00084 <li>Draw the Screen object's entire widget hierarchy.</li> 00085 </ul> 00086 <li>Destroy application-specific objects.</li> 00087 <ul> 00088 <li>Destroy WorldViewWidget object, which will destroy WorldView object.</li> 00089 <li>Destroy World object, which will destroy all its ObjectLayers, Objects <strong>and Entities.</strong></li> 00090 </ul> 00091 </ul> 00092 <li>Delete the Screen object. Shutdown the Pal and game engine singletons.</li> 00093 </ul> 00094 </ul> 00095 00096 Comments explaining previously covered material will be made more terse or 00097 deleted entirely in each successive lesson. If something is not explained 00098 well enough, it was probably already explained in 00099 @ref lessons "previous lessons". 00100 00101 <strong>Code Diving!</strong> 00102 00103 @code */ 00104 #include "xrb.hpp" // Must be included in every source/header file. 00105 00106 #include <set> // For use of the std::set template class. 00107 00108 #include "xrb_engine2_objectlayer.hpp" // For use of the Engine2::ObjectLayer class. 00109 #include "xrb_engine2_physicshandler.hpp" // For use of the Engine2::PhysicsHandler class. 00110 #include "xrb_engine2_sprite.hpp" // For use of the Engine2::Sprite class. 00111 #include "xrb_engine2_world.hpp" // For use of the Engine2::World class. 00112 #include "xrb_engine2_worldview.hpp" // For use of the Engine2::WorldView class. 00113 #include "xrb_engine2_worldviewwidget.hpp" // For use of the Engine2::WorldViewWidget class. 00114 #include "xrb_event.hpp" // For use of the Event classes. 00115 #include "xrb_eventqueue.hpp" // For use of the EventQueue class. 00116 #include "xrb_inputstate.hpp" // For use of the InputState class (via Singleton::). 00117 #include "xrb_input_events.hpp" // For use of the EventMouseWheel class. 00118 #include "xrb_math.hpp" // For use of the functions in the Math namespace. 00119 #include "xrb_screen.hpp" // For use of the necessary Screen widget class. 00120 #include "xrb_sdlpal.hpp" // For use of the SDLPal platform abstraction layer. 00121 00122 using namespace Xrb; // To avoid having to use Xrb:: everywhere. 00123 00124 // AwesomeEntity remains unchanged relative to the @ref lesson05 "previous lesson". 00125 class AwesomeEntity : public Engine2::Entity 00126 { 00127 public: 00128 00129 // The constructor simply initializes the properties to sane values. Mass 00130 // must be greater than zero to avoid division by zero in some calculations. 00131 AwesomeEntity () 00132 : 00133 Engine2::Entity(), 00134 m_mass(1.0f), 00135 m_velocity(FloatVector2::ms_zero), 00136 m_force(FloatVector2::ms_zero) 00137 { } 00138 00139 // Trivial accessors for the properties of AwesomeEntity. 00140 inline Float Mass () const { return m_mass; } 00141 inline FloatVector2 const &Velocity () const { return m_velocity; } 00142 inline FloatVector2 const &Force () const { return m_force; } 00143 00144 // Modifiers for the properties of AwesomeEntity. The ASSERT_NAN_SANITY_CHECK 00145 // macro is used in various places in Engine2 code to quickly catch common 00146 // bugs in game code which result in NaN values being fed to the snake. 00147 inline void SetMass (Float mass) 00148 { 00149 ASSERT_NAN_SANITY_CHECK(Math::IsFinite(mass)); 00150 ASSERT1(mass > 0.0f); 00151 m_mass = mass; 00152 } 00153 inline void SetVelocity (FloatVector2 const &velocity) 00154 { 00155 ASSERT_NAN_SANITY_CHECK(Math::IsFinite(velocity[Dim::X])); 00156 ASSERT_NAN_SANITY_CHECK(Math::IsFinite(velocity[Dim::Y])); 00157 m_velocity = velocity; 00158 } 00159 00160 // Procedures which will be used by the gravity calculations in AwesomeWorld. 00161 void IncrementVelocity (FloatVector2 const &velocity_delta) 00162 { 00163 ASSERT_NAN_SANITY_CHECK(Math::IsFinite(velocity_delta[Dim::X])); 00164 ASSERT_NAN_SANITY_CHECK(Math::IsFinite(velocity_delta[Dim::Y])); 00165 m_velocity += velocity_delta; 00166 } 00167 void IncrementForce (FloatVector2 const &force_delta) 00168 { 00169 ASSERT_NAN_SANITY_CHECK(Math::IsFinite(force_delta[Dim::X])); 00170 ASSERT_NAN_SANITY_CHECK(Math::IsFinite(force_delta[Dim::Y])); 00171 m_force += force_delta; 00172 } 00173 void ResetForce () { m_force = FloatVector2::ms_zero; } 00174 00175 // These are pure virtual methods which have to be implemented in Entity 00176 // subclasses. Write can be left blank for our purposes. 00177 virtual void Write (Serializer &serializer) const { } 00178 // This method is called on entities which have hit the side of the 00179 // ObjectLayer. For now we'll just stop the entity's motion along the 00180 // indicated dimension(s). This method will only be called on entities 00181 // in a non-wrapped ObjectLayer. 00182 virtual void HandleObjectLayerContainment (bool component_x, bool component_y) 00183 { 00184 if (component_x) 00185 m_velocity[Dim::X] = 0.0f; 00186 if (component_y) 00187 m_velocity[Dim::Y] = 0.0f; 00188 } 00189 00190 protected: 00191 00192 // These are pure virtual methods which have to be implemented in Entity 00193 // subclasses. For our purposes, they can be left empty. 00194 virtual void HandleNewOwnerObject () { } 00195 virtual void CloneProperties (Entity const *entity) { } 00196 00197 private: 00198 00199 Float m_mass; 00200 FloatVector2 m_velocity; 00201 FloatVector2 m_force; 00202 }; // end of class AwesomeEntity 00203 00204 /* @endcode 00205 PhysicsHandler can be thought of as having a very simple relationship with 00206 World. It tracks two things: ObjectLayer and Entity. Any time an 00207 ObjectLayer is added to the world, it lets the PhysicsHandler know. Any time 00208 an Entity is added or removed from the world, it lets the PhysicsHandler know. 00209 The only other consideration is that World will call ProcessFrame on the 00210 PhysicsHandler, so that all physics code has a chance to run once per frame 00211 in HandleFrame. 00212 00213 The only thing provided by the PhysicsHandler baseclass is a pointer to the 00214 World which owns it. The rest is a blank slate on which a programmer can 00215 implement whatever retarded designs they see fit. 00216 00217 In this lesson, we will yank the physics code out of the previous lesson's 00218 AwesomeWorld::HandleFrame and dump it, functionally unchanged, into 00219 AwesomePhysicsHandler::HandleFrame. The physics behavior as seen by 00220 the user will not change. 00221 @code */ 00222 class AwesomePhysicsHandler : public Engine2::PhysicsHandler 00223 { 00224 public: 00225 00226 // The constructor initializes m_gravitational_constant. 00227 AwesomePhysicsHandler (Float gravitational_constant) 00228 : 00229 Engine2::PhysicsHandler(), 00230 m_gravitational_constant(gravitational_constant) 00231 { } 00232 // The destructor will just assert that there are no remaining entities. 00233 virtual ~AwesomePhysicsHandler () 00234 { 00235 ASSERT1(m_entity_set.empty()); 00236 } 00237 00238 // Trivial accessor for m_gravitational_constant. 00239 inline Float GravitationalConstant () const { return m_gravitational_constant; } 00240 // Helper function to calculate Newton's Law Of Universal Gravitation. 00241 Float CalculateGravitationalForce (AwesomeEntity *entity0, AwesomeEntity *entity1) const 00242 { 00243 ASSERT1(entity0 != NULL && entity1 != NULL); 00244 FloatVector2 entity_offset(entity1->Translation() - entity0->Translation()); 00245 Float distance = entity_offset.Length(); 00246 // If they're touching, don't apply gravitational force (this 00247 // is to avoid a divide by zero if their positions coincide). 00248 if (distance < entity0->ScaleFactor() + entity1->ScaleFactor()) 00249 return 0.0f; 00250 else 00251 return 00252 m_gravitational_constant * 00253 entity0->Mass() * entity1->Mass() / 00254 (distance * distance); 00255 } 00256 00257 /* @endcode 00258 These two virtual methods are provided to indicate all created ObjectLayers 00259 to the PhysicsHandler, so that if desired in a particular implementation, 00260 physics calculations can be handled separately for different ObjectLayers. 00261 For example, in a game with ground and sky layers, the objects on the 00262 ground should not collide with the objects in the sky. Determining which 00263 ObjectLayer an Entity belongs to is done in AddEntity and RemoveEntity 00264 (see below). 00265 00266 In this lesson, we assume that all entities will be in the foreground 00267 ObjectLayer, so we will not track ObjectLayers. 00268 @code */ 00269 virtual void AddObjectLayer (Engine2::ObjectLayer *object_layer) { } 00270 virtual void SetMainObjectLayer (Engine2::ObjectLayer *object_layer) { } 00271 00272 /* @endcode 00273 This method is called by the World object when a dynamic object is added. 00274 This is necessary because because the PhysicsHandler is responsible for 00275 tracking entities on its own. 00276 00277 In this lesson, we are keeping a std::set of all added entities. The 00278 std::set template class was chosen because adding/removing elements is 00279 O(log(n)). 00280 @code */ 00281 virtual void AddEntity (Engine2::Entity *entity) 00282 { 00283 ASSERT1(entity != NULL); 00284 ASSERT1(dynamic_cast<AwesomeEntity *>(entity) != NULL); 00285 m_entity_set.insert(static_cast<AwesomeEntity *>(entity)); 00286 } 00287 /* @endcode 00288 The removal analog to AddEntity. This method is provided so the 00289 PhysicsHandler can clean up its own entity-tracking when a dynamic 00290 object is removed from the World. 00291 @code */ 00292 virtual void RemoveEntity (Engine2::Entity *entity) 00293 { 00294 ASSERT1(entity != NULL); 00295 ASSERT1(dynamic_cast<AwesomeEntity *>(entity) != NULL); 00296 ASSERT1(m_entity_set.find(static_cast<AwesomeEntity *>(entity)) != m_entity_set.end()); 00297 m_entity_set.erase(static_cast<AwesomeEntity *>(entity)); 00298 } 00299 00300 protected: 00301 00302 /* @endcode 00303 The physics code that was in AwesomeWorld::HandleFrame in the 00304 @ref lesson05 "previous lesson" has been moved into this method, with the 00305 for-loop iteration changed to accomodate the std::set which stores the 00306 entities being tracked by this PhysicsHandler. 00307 @code */ 00308 virtual void HandleFrame () 00309 { 00310 EntitySet::iterator it_end = m_entity_set.end(); 00311 00312 // Apply gravitational forces between each distinct pair of entities. 00313 for (EntitySet::iterator it0 = m_entity_set.begin(); 00314 it0 != it_end; 00315 ++it0) 00316 { 00317 AwesomeEntity *entity0 = *it0; 00318 ASSERT1(entity0 != NULL); 00319 00320 for (EntitySet::iterator it1 = it0; 00321 it1 != it_end; 00322 ++it1) 00323 { 00324 AwesomeEntity *entity1 = *it1; 00325 ASSERT1(entity1 != NULL); 00326 00327 // Skip this calculation if the pair is an entity with itself. 00328 if (entity0 == entity1) 00329 continue; 00330 00331 // Use the helper function to calculate the gravitational force 00332 // between the two entities. 00333 Float gravitational_force = CalculateGravitationalForce(entity0, entity1); 00334 ASSERT1(gravitational_force >= 0.0f); 00335 // If the force is zero (which can happen when the entities' 00336 // centers coincide and the gravitation equation would divide 00337 // by zero), skip this entity pair. 00338 if (gravitational_force == 0.0f) 00339 continue; 00340 00341 // The gravitational force is from entity0 to entity1 00342 FloatVector2 force_direction = (entity1->Translation() - entity0->Translation()).Normalization(); 00343 // Apply equal and opposite gravitational force to both entities. 00344 entity0->IncrementForce( gravitational_force * force_direction); 00345 entity1->IncrementForce(-gravitational_force * force_direction); 00346 } 00347 } 00348 00349 // Update the velocity vector of each entity with the accumulated force 00350 // and update the position vector using the newly calculated velocity. 00351 for (EntitySet::iterator it = m_entity_set.begin(); 00352 it != it_end; 00353 ++it) 00354 { 00355 AwesomeEntity *entity = *it; 00356 ASSERT1(entity != NULL); 00357 00358 ASSERT1(entity->Mass() > 0.0f); 00359 // Use Euler Integration to calculate the new velocity, based on 00360 // the accumulated force during this frame. 00361 entity->IncrementVelocity(entity->Force() / entity->Mass() * FrameDT()); 00362 // Reset the accumulated force for next frame. 00363 entity->ResetForce(); 00364 // Use Euler Integration again to calculate the new position, 00365 // based on the entity's velocity. 00366 entity->Translate(entity->Velocity() * FrameDT()); 00367 } 00368 } 00369 00370 private: 00371 00372 typedef std::set<AwesomeEntity *> EntitySet; 00373 00374 EntitySet m_entity_set; 00375 Float m_gravitational_constant; 00376 }; // end of class AwesomePhysicsHandler 00377 00378 class AwesomeWorld : public Engine2::World 00379 { 00380 public: 00381 00382 /* @endcode 00383 The difference in this constructor from the @ref lesson05 "previous lesson" 00384 is that a background ObjectLayer, populated with "stars" is created before 00385 the foreground ObjectLayer. The Z depth of the layer is set to a high value 00386 so that the parallax effect is noticeable. The Z depth causes the ObjectLayer 00387 to appear close up/far away, so changing the size of the ObjectLayer itself 00388 is sometimes necessary to get the effect you want. In this case, since the 00389 layer is far off in the distance, its size has been set to a large value 00390 relative to the foreground layer. 00391 00392 Also notice that the superclass constructor is being passed a new'ed 00393 instance of AwesomePhysicsHandler. This is the PhysicsHandler implementation 00394 which will be passed all Entity instances which are added to the world 00395 and whose responsibility is to update their application-specific 00396 functionality -- not just position/velocity/etc but also any "think" 00397 functions which may be implemented through Entity, and other things of 00398 that type. 00399 @code */ 00400 AwesomeWorld () 00401 : 00402 Engine2::World(new AwesomePhysicsHandler(60.0f)) 00403 { 00404 // At this point, the world is empty. 00405 00406 Float object_layer_side_length; 00407 Engine2::ObjectLayer *object_layer; 00408 Engine2::Sprite *sprite; 00409 00410 // Here we will create an ObjectLayer to contain the starfield 00411 // which will constitute the background. 00412 object_layer_side_length = 10000.0f; 00413 object_layer = 00414 Engine2::ObjectLayer::Create( 00415 this, // owner world 00416 false, // not wrapped 00417 object_layer_side_length, // side length 00418 6, // visibility quad tree depth 00419 1000.0f); // z depth 00420 AddObjectLayer(object_layer); 00421 00422 // Add a bunch of "star" sprites. 00423 static Uint32 const s_star_count = 100; 00424 for (Uint32 i = 0; i < s_star_count; ++i) 00425 { 00426 sprite = Engine2::Sprite::Create("resources/shade0_small.png"); 00427 sprite->SetScaleFactor(Math::RandomFloat(0.0016f, 0.0024f) * object_layer_side_length); 00428 sprite->SetTranslation( 00429 object_layer_side_length * 00430 FloatVector2( 00431 Math::RandomFloat(-0.5f, 0.5f), 00432 Math::RandomFloat(-0.5f, 0.5f))); 00433 AddStaticObject(sprite, object_layer); 00434 } 00435 00436 // Create the ObjectLayer which will hold our game objects. 00437 object_layer_side_length = 2000.0f; 00438 object_layer = 00439 Engine2::ObjectLayer::Create( 00440 this, // owner world 00441 false, // not wrapped 00442 object_layer_side_length, // side length 00443 6, // visibility quad tree depth 00444 0.0f); // z depth 00445 AddObjectLayer(object_layer); 00446 SetMainObjectLayer(object_layer); 00447 00448 AwesomeEntity *planet; 00449 00450 // Create a large, heavy planet. 00451 sprite = Engine2::Sprite::Create("resources/demi3_small.png"); 00452 planet = new AwesomeEntity(); 00453 sprite->SetEntity(planet); 00454 planet->SetTranslation(FloatVector2::ms_zero); 00455 planet->SetScaleFactor(250.0f); 00456 planet->SetMass(100.0f * planet->ScaleFactor() * planet->ScaleFactor()); 00457 AddDynamicObject(sprite, object_layer); 00458 00459 // Create a bunch of small, light moons. 00460 static Uint32 const s_moon_count = 50; 00461 for (Uint32 i = 0; i < s_moon_count; ++i) 00462 { 00463 sprite = Engine2::Sprite::Create("resources/shade3_small.png"); 00464 AwesomeEntity *moon = new AwesomeEntity(); 00465 sprite->SetEntity(moon); 00466 sprite->SetZDepth(-0.1f); 00467 moon->SetScaleFactor(Math::RandomFloat(10.0f, 20.0f)); 00468 moon->SetMass(0.01f * moon->ScaleFactor() * moon->ScaleFactor()); 00469 // Pick a distance to orbit the moon at. 00470 Float minimum_orbital_radius = planet->ScaleFactor() + moon->ScaleFactor() + 100.0f; 00471 Float orbital_radius = Math::RandomFloat(minimum_orbital_radius, minimum_orbital_radius + 400.0f); 00472 ASSERT1(orbital_radius > 0.0f); 00473 // The moon will be placed randomly using polar coordinates. 00474 // We've calculated the R value, now we need theta. 00475 Float angle = Math::RandomFloat(0.0f, 360.0f); 00476 // Initialize the moon's position 00477 moon->SetTranslation(orbital_radius * Math::UnitVector(angle)); 00478 // In order to figure out what speed to use to set the moon into 00479 // circular orbit, we need to know the magnitude of the gravitational 00480 // force between it and the large planet. 00481 Float gravitational_force = GetPhysicsHandler()->CalculateGravitationalForce(planet, moon); 00482 // Solve for the necessary orbital speed using Kepler's Third Law. 00483 Float orbital_speed = Math::Sqrt(gravitational_force * orbital_radius / moon->Mass()); 00484 // The velocity must be perpendicular to the vector joining the 00485 // centers of the planet and the moon. 00486 moon->SetVelocity(orbital_speed * Math::UnitVector(angle+90.0f)); 00487 // Finally add it to the world. 00488 AddDynamicObject(sprite, object_layer); 00489 } 00490 } 00491 00492 protected: 00493 00494 // Trivial accessor to retrieve a pointer to the World's AwesomePhysicsHandler. 00495 inline AwesomePhysicsHandler *GetPhysicsHandler () { return dynamic_cast<AwesomePhysicsHandler *>(m_physics_handler); } 00496 00497 /* @endcode 00498 Since we have moved all the physics code into the HandleFrame method of 00499 AwesomePhysicsHandler, we no longer need to do it here. However, this is 00500 where you could put game flow/logic code (e.g. spawning ships, incrementing 00501 a player's score, etc). 00502 @code */ 00503 virtual void HandleFrame () 00504 { 00505 // You must always call the superclass' HandleFrame method, as it 00506 // performs vital processing -- specifically of the EventQueue and 00507 // PhysicsHandler, which will be covered in a later lesson. 00508 Engine2::World::HandleFrame(); 00509 } 00510 }; // end of class AwesomeWorld 00511 00512 /* @endcode 00513 The rest of the code is identical to the previous lesson. 00514 @code */ 00515 // AwesomeWorldView is unchanged relative to the previous lesson. 00516 class AwesomeWorldView : public Engine2::WorldView 00517 { 00518 public: 00519 00520 // Trivial constructor which is just a frontend for WorldView's constructor. 00521 AwesomeWorldView (Engine2::WorldViewWidget *parent_world_view_widget) 00522 : 00523 Engine2::WorldView(parent_world_view_widget) 00524 { } 00525 00526 // Called by WorldViewWidget with all mouse wheel events for this WorldView 00527 virtual bool ProcessMouseWheelEvent (EventMouseWheel const *e) 00528 { 00529 // Rotate the view on ALT+mouse-wheel-up/down. 00530 if (e->IsEitherAltKeyPressed()) 00531 RotateView((e->ButtonCode() == Key::MOUSEWHEELUP) ? -15.0f : 15.0f); 00532 // Otherwise, we will zoom the view on mouse-wheel-up/down. 00533 else 00534 ZoomView((e->ButtonCode() == Key::MOUSEWHEELUP) ? 1.2f : 1.0f / 1.2f); 00535 // Indicates that the event was used by this method. 00536 return true; 00537 } 00538 // This method is the mouse motion analog of ProcessMouseWheelEvent. 00539 virtual bool ProcessMouseMotionEvent (EventMouseMotion const *e) 00540 { 00541 // Only do stuff if the left mouse button was pressed for this event. 00542 if (e->IsLeftMouseButtonPressed()) 00543 { 00544 // Move the view by a delta which is calculated by transforming 00545 // the screen coordinates of the event to world coordinates as used 00546 // by WorldView. 00547 MoveView( 00548 ParallaxedScreenToWorld() * FloatVector2::ms_zero - 00549 ParallaxedScreenToWorld() * e->Delta().StaticCast<Float>()); 00550 // Indicates that the event was used by this method. 00551 return true; 00552 } 00553 else 00554 // Event not used. 00555 return false; 00556 } 00557 }; // end of class AwesomeWorldView 00558 00559 void CleanUp () 00560 { 00561 fprintf(stderr, "CleanUp();\n"); 00562 // Shutdown the Pal and singletons. 00563 Singleton::Pal().Shutdown(); 00564 Singleton::Shutdown(); 00565 } 00566 00567 int main (int argc, char **argv) 00568 { 00569 fprintf(stderr, "main();\n"); 00570 00571 // Initialize engine singletons. 00572 Singleton::Initialize(SDLPal::Create, "none"); 00573 // Initialize the Pal. 00574 if (Singleton::Pal().Initialize() != Pal::SUCCESS) 00575 return 1; 00576 // Set the window caption. 00577 Singleton::Pal().SetWindowCaption("XuqRijBuh Lesson 06"); 00578 // Create Screen object and initialize given video mode. 00579 Screen *screen = Screen::Create(800, 600, 32, false); 00580 // If the Screen failed to initialize, print an error message and quit. 00581 if (screen == NULL) 00582 { 00583 fprintf(stderr, "unable to initialize video mode\n"); 00584 CleanUp(); 00585 return 2; 00586 } 00587 00588 // Here is where the application-specific code begins. 00589 { 00590 // Create our sweet game world via a call to CreateAndPopulateWorld. 00591 AwesomeWorld *world = new AwesomeWorld(); 00592 // Create the WorldViewWidget as a child of screen. This is what will 00593 // contain an instance of WorldView and will cause it to be rendered. 00594 Engine2::WorldViewWidget *world_view_widget = new Engine2::WorldViewWidget(screen); 00595 screen->SetMainWidget(world_view_widget); 00596 // Create an instance of our AwesomeWorldView, using the newly created 00597 // WorldViewWidget as its "parent". Set the zoom factor so something 00598 // reasonable (though arbitrary). 00599 AwesomeWorldView *world_view = new AwesomeWorldView(world_view_widget); 00600 world_view->SetZoomFactor(0.002f); 00601 // Attach the newly created WorldView to the World object. 00602 world->AttachWorldView(world_view); 00603 00604 // These values will be used below in the framerate control code. 00605 Float current_real_time = 0.0f; 00606 Float next_real_time = 0.0f; 00607 Float desired_framerate = 60.0f; 00608 // Run the game loop until the Screen no longer has the will to live. 00609 while (!screen->IsQuitRequested()) 00610 { 00611 // Get the current real time and figure out how long to sleep, then sleep. 00612 current_real_time = 0.001f * Singleton::Pal().CurrentTime(); 00613 Sint32 milliseconds_to_sleep = Max(0, static_cast<Sint32>(1000.0f * (next_real_time - current_real_time))); 00614 Singleton::Pal().Sleep(milliseconds_to_sleep); 00615 // Calculate the desired next game loop time 00616 next_real_time = Max(current_real_time, next_real_time + 1.0f / desired_framerate); 00617 00618 // Process events until there are no more. 00619 Event *event = NULL; 00620 while ((event = Singleton::Pal().PollEvent(screen, current_real_time)) != NULL) 00621 { 00622 // Let the InputState singleton "have a go" at keyboard/mouse events. 00623 if (event->IsKeyEvent() || event->IsMouseButtonEvent()) 00624 Singleton::InputState().ProcessEvent(event); 00625 // Give the GUI hierarchy a chance at the event and then delete it. 00626 screen->ProcessEvent(event); 00627 Delete(event); 00628 } 00629 00630 // Perform all off-screen game processing. 00631 world->ProcessFrame(current_real_time); 00632 00633 // Turn the EventQueue crank, Perform off-screen GUI processing, 00634 // turn the EventQueue crank again, and then draw everything. 00635 screen->OwnerEventQueue()->ProcessFrame(current_real_time); 00636 screen->ProcessFrame(current_real_time); 00637 screen->OwnerEventQueue()->ProcessFrame(current_real_time); 00638 screen->Draw(); 00639 } 00640 00641 // Delete world_view_widget and world, in that order. This will 00642 // automatically delete our AwesomeWorldView instance. 00643 Delete(world_view_widget); 00644 Delete(world); 00645 } 00646 00647 // Delete the Screen (and GUI hierarchy), "SHUT IT DOWN", and return success. 00648 Delete(screen); 00649 CleanUp(); 00650 return 0; 00651 } 00652 /* @endcode 00653 00654 <strong>Exercises</strong> 00655 00656 <ul> 00657 <li>Left-click and drag the view around to see the parallaxing effect 00658 between the two ObjectLayers.</li> 00659 <li>Add more starfield ObjectLayers at different z depths</li> 00660 <li>Using file "resources/explosion1a_small.png" as nebulae, add a 00661 foreground nebula field with an ObjectLayer Z depth of a negative 00662 value which will put it in front of the main object layer. 00663 Move the view around and see the effect. Now zoom the view in 00664 and out and see what the effect is on the nebula layer.</li> 00665 </ul> 00666 00667 Thus concludes lesson06. \f$ \displaystyle \sum_{i=0}^{6}lesson_{i}\approx0 \f$ 00668 */