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