Lesson3: Events and Physics

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