In Lesson 3 you'll write a physics engine application:
/////////////////////////////////////////////////////////////// // Lesson3: Physics engine and event functions /////////////////////////////////////////////////////////////// #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <stdlib.h> #include <malloc.h> #include <memory.h> #include <tchar.h> #include "adll.h" // some global definitions - we'll use them later ENGINE_VARS *ev; ENTITY *eBall; void Kick(void); void Plop(void); /////////////////////////////////////////////////////////////// int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { ev = engine_open(NULL); if (!ev) return 1; // acknex.dll not found // We add some folders that contain our media files - // replace them by your own folders! add_folder("c:\\project\\acknex\\work\\"); add_folder("c:\\project\\acknex\\template\\"); // Activate stencil shadows - they are a 'redefine' variable // that has to be set before the first engine frame - // and set the sound at full volume. SETV(shadow_stencil,ON); SETV(sound_vol,100); // Create the required splash screen panel at layer 1. // The panel definition only contains a single bmap here. // Engine functions use variables of type var, so we // precede all number arguments with the _VAR macro. PANEL* pSplash = pan_create("bmap = logolite.pcx;",_VAR(1)); // Scale the panel by the screen to bmap size ratio // in order to fit the screen, and make it visible. pSplash->scale_x = _VAR((float)v(screen_size).x / bmap_width(pSplash->bmap)); pSplash->scale_y = _VAR((float)v(screen_size).y / bmap_height(pSplash->bmap)); pSplash->flags |= SHOW; // After a panel is set to SHOW, we have to wait 3 frames // until we can really see it on the screen. // The first frame paints it into the background buffer, // two more frames are needed until the background buffer // is flipped to the front in a triple buffer system. for (int i=0; i<3; i++) engine_frame(); // Before we can create level entities, a level must be loaded. // We'll use the small terrain from the techdemo for a level. // We have to wait one more engine frame to ensure that the level // exists and is ready to be filled with level entities. level_load("small.hmp"); engine_frame(); // Now we can create a layer entity for a sky cube at layer 0. // The SKY flag tells the engine that it's a sky entity, // and the CUBE flag tells that the image is a six-sided cube. ent_createlayer("blood_gorge+6.tga",_VAR(SKY|CUBE),_VAR(0)); // Let's now create a ball at position (0,0,100). // The _vec function converts 3 floats to a temporary var vector // for passing positions to engine functions. eBall = ent_create("earth.mdl",_vec(0,0,100),NULL); // Now let's set the ball's physical properties. // We add a small speed to give it a little sidewards kick. phent_settype(eBall,_VAR(PH_RIGID),_VAR(PH_SPHERE)); phent_setmass(eBall,_VAR(1.0),_VAR(PH_SPHERE)); phent_setfriction(eBall,_VAR(90.0)); phent_setelasticity(eBall,_VAR(75.0),_VAR(100.0)); phent_setdamping(eBall,_VAR(30.0),_VAR(5.0)); phent_addvelcentral(eBall,_vec(2,2,0)); // A ball game would be no fun without gravity. ph_setgravity(_vec(0,0,-500)); // We are setting two entity flags in order to cast, but not receive // dynamic shadows for the ball eBall->flags |= SHADOW|CAST; // We want to kick the ball by pressing the space key. For this we could scan // the key state in the main loop; however a more elegant way is a key event. // We can assign functions to certain events like hitting a key, or a // collision in the game. v(on_space) = (EVENT)Kick; // Another event: if the ball hits something, a sound shall be played. // We set the event function and the enable_friction mask for triggering // the event at physics collisions. Note that the minimum speed - // the third parameter of phent_setelasticity - determines the // sensitivity of this event. eBall->event = (EVENT)Plop; eBall->emask |= ENABLE_FRICTION; // Now that everything is set up, remove the splash screen. pan_remove(pSplash); // During the main loop we're just moving the camera, as before. while (engine_frame()) { // For the camera movement we now use a more sophisticated method // with the vec_accelerate() function. It accelerates a speed and // is not dependent on the frame rate - so we don't need to // limit the fps in this example. This code is equivalent // to the built-in camera movement, but uses different keys. static VECTOR vSpeed = { 0,0,0 }, vAngularSpeed = { 0,0,0 }; VECTOR vForce, vMove; // We need static vectors for the speeds here because they must be // preserved between loops. vForce.x = -5*(v(key_force).x + v(mouse_force).x); // pan angle vForce.y = 5*(v(key_force).y + v(mouse_force).y); // tilt angle vForce.z = 0; // roll angle vec_accelerate(&vMove,&vAngularSpeed,&vForce,_VAR(0.8)); vec_add((VECTOR*)&v(camera).pan,&vMove); const int iSpeed = 6; vForce.x = iSpeed * (v(key_w) - v(key_s)); // forward vForce.y = iSpeed * (v(key_a) - v(key_d)); // sideward vForce.z = iSpeed * (v(key_home) - v(key_end)); // upward vec_accelerate(&vMove,&vSpeed,&vForce,_VAR(0.5)); vec_rotate(&vMove,(ANGLE*)&v(camera).pan); vec_add((VECTOR*)&v(camera).x,&vMove); } // We don't need to free our created entities, bitmaps and sounds. // The engine does this automatically when closing. engine_close(); return 0; } // This is our event function for the ball impact. void Plop(void) { // Create a ball impact sound. // Use a static pointer for creating it only once. static SOUND* sPong = snd_create("wham.wav"); // Play the sound at the event entities' position and speed. ent_playsound(eBall,sPong,_VAR(100)); } // This is our event function for hitting the [Space] key. void Kick(void) { // Create a speed vector and rotate it in camera direction. VECTOR vSpeed = { _VAR(150),_VAR(0),_VAR(0) }; vec_rotate(&vSpeed,(ANGLE*)&v(camera).pan); // Add a vertical speed to give the ball an upwards kick. vSpeed.z = _VAR(75); // Now apply the speed to the ball, and play a hit sound. phent_addvelcentral(eBall,&vSpeed); Plop(); }► latest version online