Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
DZGame.c
/* |
* File: DZGame.c |
* |
* Contents: Handles the game play. |
* |
* Copyright © 1996 Apple Computer, Inc. |
*/ |
#include <assert.h> |
#include <string.h> |
#include <Events.h> |
#include <Fonts.h> |
#include <QDOffscreen.h> |
#include <Timer.h> |
#include <Types.h> |
#include <QD3D.h> |
#include <QD3DGeometry.h> |
#include <QD3DMath.h> |
#include <QD3DSet.h> |
#include "SoundSprocket.h" |
#include "DZDisplay.h" |
#include "DZDrone.h" |
#include "DZGame.h" |
#include "DZInput.h" |
#include "DZSound.h" |
#include "DZSpace.h" |
#ifndef ONE_DRONE |
#define ONE_DRONE 0 |
#endif |
#ifndef HUD_SHOWS_VELOCITY_VECTOR |
#define HUD_SHOWS_VELOCITY_VECTOR 1 |
#endif |
#define DEFAULT_INTERVAL 0.05 // Initial frame rate -- just a guess |
#define MAX_INTERVAL 0.25 // Limit on seconds per frame |
#define INV_SQRT_TWO 0.707 |
#define ROLL_RATE 3.0 // Radians per second for full joystick throw |
#define PITCH_RATE 1.0 // Radians per second for full joystick throw |
#define YAW_RATE 3.0 // Radians per second for full joystick throw |
#define ENGINE_RATING 1.0 // Percentage engine is more/less powerful |
#define DAMPING_PERCENT 0.90 // as a percentage (1.00 would be no damping) |
#define TICKS_PER_AUTOFIRE_SHOT 60 // numbers of ticks until next auto-repeat shot |
enum { |
#if ONE_DRONE |
kGameAutoDroneCount = 1 // Number of autopilot drones |
#else |
kGameAutoDroneCount = 4 // Number of autopilot drones |
#endif |
}; |
float gGameInterval = DEFAULT_INTERVAL; |
float gGameFramesPerSecond = 1.0/DEFAULT_INTERVAL; |
Boolean gSoundOn = true; |
static TGameState gGameState = kGameState_Stopped; |
static unsigned long gGameTime = 0; // Last frame time |
static Boolean gGameHUDVisible = true; |
static TDroneObject gGameSelfDrone = NULL; |
static GWorldPtr gGameFPSGWorld = NULL; |
static TQ3GeometryObject gGameFPSMarker = NULL; |
static TQ3MarkerData gGameFPSMarkerData; |
static Boolean gGameFPSVisible = false; |
static GWorldPtr gGameThrottleGWorld = NULL; |
static TQ3GeometryObject gGameThrottleMarker = NULL; |
static TQ3MarkerData gGameThrottleMarkerData; |
static Boolean gGameThrottleVisible = false; |
static GWorldPtr gGameVelocityGWorld = NULL; |
static TQ3GeometryObject gGameVelocityMarker = NULL; |
static TQ3MarkerData gGameVelocityMarkerData; |
static Boolean gGameVelocityVisible = true; |
static Boolean gInertialDampersOn = false; |
static Boolean gFiringOn = false; |
static UInt32 gLastShotTicks = 0; |
static TQ3GeometryObject gGameCrossHairs = NULL; |
static unsigned long gGameCrossHairsData[32] = { |
0x00000000, 0x00000000, 0x00000000, 0x00000000, |
0x00000000, 0x00010000, 0x00000000, 0x00010000, |
0x00000000, 0x00054000, 0x00101000, 0x00228800, |
0x00082000, 0x00400400, 0x00101000, 0x05400540, |
0x00101000, 0x00400400, 0x00082000, 0x00228800, |
0x00101000, 0x00054000, 0x00000000, 0x00010000, |
0x00000000, 0x00010000, 0x00000000, 0x00000000, |
0x00000000, 0x00000000, 0x00000000, 0x00000000 |
}; |
/* ============================================================================= |
* Game_Init (external) |
* |
* Initializes the game stuff. |
* ========================================================================== */ |
void Game_Init( |
void) |
{ |
Rect bounds; |
PixMapHandle pixMapHandle; |
CGrafPtr savePort; |
GDHandle saveGDevice; |
TQ3MarkerData markerData; |
TQ3ColorRGB color; |
// Set up the 3D sound listener |
Sound_GetListener(); |
// Create the FPS marker |
bounds.top = 0; |
bounds.left = 0; |
bounds.bottom = 12; |
bounds.right = 31; |
gGameFPSGWorld = NULL; |
NewGWorld(&gGameFPSGWorld, 1, &bounds, NULL, NULL, 0); |
assert(gGameFPSGWorld != NULL); |
LockPixels(gGameFPSGWorld->portPixMap); |
GetGWorld(&savePort, &saveGDevice); |
SetGWorld(gGameFPSGWorld, NULL); |
TextFont(kFontIDGeneva); |
TextSize(10); |
EraseRect(&gGameFPSGWorld->portRect); |
SetGWorld(savePort, saveGDevice); |
pixMapHandle = GetGWorldPixMap(gGameFPSGWorld); |
gGameFPSMarkerData.location.x = 0.0; |
gGameFPSMarkerData.location.y = 0.0; |
gGameFPSMarkerData.location.z = 0.0; |
gGameFPSMarkerData.xOffset = -(bounds.left+bounds.right+1 >> 1); |
gGameFPSMarkerData.yOffset = -(bounds.top+bounds.bottom+1 >> 1); |
gGameFPSMarkerData.bitmap.image = (unsigned char*) GetPixBaseAddr(pixMapHandle); |
gGameFPSMarkerData.bitmap.width = bounds.right-bounds.left; |
gGameFPSMarkerData.bitmap.height = bounds.bottom-bounds.top; |
gGameFPSMarkerData.bitmap.rowBytes = (*pixMapHandle)->rowBytes & 0x00003FFF; |
gGameFPSMarkerData.bitmap.bitOrder = kQ3EndianBig; |
gGameFPSMarkerData.markerAttributeSet = Q3AttributeSet_New(); |
assert(gGameFPSMarkerData.markerAttributeSet != NULL); |
color.r = 1.0; |
color.g = 1.0; |
color.b = 0.4; |
Q3AttributeSet_Add(gGameFPSMarkerData.markerAttributeSet, kQ3AttributeTypeDiffuseColor, &color); |
gGameFPSMarker = Q3Marker_New(&gGameFPSMarkerData); |
// Create the Velocity marker |
bounds.top = 0; |
bounds.left = 0; |
bounds.bottom = 12; |
bounds.right = 31; |
gGameVelocityGWorld = NULL; |
NewGWorld(&gGameVelocityGWorld, 1, &bounds, NULL, NULL, 0); |
assert(gGameVelocityGWorld != NULL); |
LockPixels(gGameVelocityGWorld->portPixMap); |
GetGWorld(&savePort, &saveGDevice); |
SetGWorld(gGameVelocityGWorld, NULL); |
TextFont(kFontIDGeneva); |
TextSize(10); |
EraseRect(&gGameVelocityGWorld->portRect); |
SetGWorld(savePort, saveGDevice); |
pixMapHandle = GetGWorldPixMap(gGameVelocityGWorld); |
gGameVelocityMarkerData.location.x = 0.0; |
gGameVelocityMarkerData.location.y = 0.0; |
gGameVelocityMarkerData.location.z = 0.0; |
gGameVelocityMarkerData.xOffset = -(bounds.left+bounds.right+1 >> 1); |
gGameVelocityMarkerData.yOffset = -(bounds.top+bounds.bottom+1 >> 1); |
gGameVelocityMarkerData.bitmap.image = (unsigned char*) GetPixBaseAddr(pixMapHandle); |
gGameVelocityMarkerData.bitmap.width = bounds.right-bounds.left; |
gGameVelocityMarkerData.bitmap.height = bounds.bottom-bounds.top; |
gGameVelocityMarkerData.bitmap.rowBytes = (*pixMapHandle)->rowBytes & 0x00003FFF; |
gGameVelocityMarkerData.bitmap.bitOrder = kQ3EndianBig; |
gGameVelocityMarkerData.markerAttributeSet = Q3AttributeSet_New(); |
assert(gGameVelocityMarkerData.markerAttributeSet != NULL); |
color.r = 1.0; |
color.g = 1.0; |
color.b = 0.4; |
Q3AttributeSet_Add(gGameVelocityMarkerData.markerAttributeSet, kQ3AttributeTypeDiffuseColor, &color); |
gGameVelocityMarker = Q3Marker_New(&gGameVelocityMarkerData); |
// Create the crosshairs |
markerData.location.x = 0.0; |
markerData.location.y = 0.0; |
markerData.location.z = 0.0; |
markerData.xOffset = -15; |
markerData.yOffset = -15; |
markerData.bitmap.image = (unsigned char*) gGameCrossHairsData; |
markerData.bitmap.width = 31; //¥ SHOULD BE 32, BUT TO GET AROUND A APPLE QD3D ACCEL CARD DRIVER BUG... |
markerData.bitmap.height = 32; |
markerData.bitmap.rowBytes = 4; |
markerData.bitmap.bitOrder = kQ3EndianBig; |
markerData.markerAttributeSet = Q3AttributeSet_New(); |
assert(markerData.markerAttributeSet != NULL); |
color.r = 1.0; |
color.g = 1.0; |
color.b = 0.4; |
Q3AttributeSet_Add(markerData.markerAttributeSet, kQ3AttributeTypeDiffuseColor, &color); |
gGameCrossHairs = Q3Marker_New(&markerData); |
Q3Object_Dispose(markerData.markerAttributeSet); |
markerData.markerAttributeSet = NULL; |
} |
/* ============================================================================= |
* Game_Exit (external) |
* |
* Prepares for exit. |
* ========================================================================== */ |
void Game_Exit( |
void) |
{ |
if (gGameFPSMarker != NULL) |
{ |
Q3Object_Dispose(gGameFPSMarker); |
gGameFPSMarker = NULL; |
} |
if (gGameVelocityMarker != NULL) |
{ |
Q3Object_Dispose(gGameVelocityMarker); |
gGameVelocityMarker = NULL; |
} |
if (gGameCrossHairs != NULL) |
{ |
Q3Object_Dispose(gGameCrossHairs); |
gGameCrossHairs = NULL; |
} |
} |
/* ============================================================================= |
* Game_GetState (external) |
* |
* Returns the game state. |
* ========================================================================== */ |
TGameState Game_GetState( |
void) |
{ |
return gGameState; |
} |
/* ============================================================================= |
* Game_SetState (external) |
* |
* Changes the game state. |
* ========================================================================== */ |
void Game_SetState( |
TGameState inGameState) |
{ |
TGameState prevGameState; |
TDroneObject drone; |
int droneNum; |
if (gGameState != inGameState) |
{ |
prevGameState = gGameState; |
gGameState = inGameState; |
switch (gGameState) |
{ |
case kGameState_Playing: |
// Begin Playing |
if (prevGameState == kGameState_Stopped) |
{ |
gGameSelfDrone = SelfDrone_New(); |
// Create some automatic drones |
for (droneNum = 0; droneNum < kGameAutoDroneCount; droneNum++) |
{ |
AutoDrone_New(gGameSelfDrone); |
} |
} |
Display_DrawContents(); |
Input_Activate(true); |
break; |
case kGameState_Paused: |
// From Playing to Paused |
assert(prevGameState != kGameState_Stopped); |
Game_Silence(); |
Display_DrawContents(); |
Input_Activate(false); |
break; |
case kGameState_Stopped: |
// From Playing or Paused to Stopped |
while ((drone = Drone_Next(NULL)) != NULL) |
{ |
Drone_Dispose(drone); |
} |
gGameSelfDrone = NULL; |
Display_DrawContents(); |
Input_Activate(false); |
break; |
} |
} |
} |
/* ============================================================================= |
* Game_Process (external) |
* |
* Handles idle time by moving the game ahead one time step. Only called if |
* the game is in "play" state. |
* ========================================================================== */ |
void Game_Process( |
void) |
{ |
UnsignedWide wide; |
unsigned long now; |
Str15 str; |
CGrafPtr savePort; |
GDHandle saveGDevice; |
TDroneObject drone; |
TDroneObject next; |
TQ3Point3D position; |
TQ3Vector3D direction; |
TQ3Vector3D up; |
TQ3Matrix4x4 matrix; |
Boolean ok; |
UInt32 count; |
// Find the frame rate |
Microseconds(&wide); |
now = wide.lo; |
if (gGameTime != 0) |
{ |
// Find the interval for the last frame |
gGameInterval = 0.000001*(now-gGameTime); |
// Limit frame rate to a resonable number |
if (gGameInterval > MAX_INTERVAL) |
{ |
gGameInterval = MAX_INTERVAL; |
} |
// Find corresponding frames per second |
gGameFramesPerSecond = 1.0/gGameInterval; |
} |
gGameTime = now; |
// Update the FPS marker |
if (gGameFPSVisible) |
{ |
sprintf((char*) str, "x%.1f", gGameFramesPerSecond); |
str[0] = strlen((char*) str) - 1; |
GetGWorld(&savePort, &saveGDevice); |
SetGWorld(gGameFPSGWorld, NULL); |
EraseRect(&gGameFPSGWorld->portRect); |
MoveTo(gGameFPSGWorld->portRect.left+gGameFPSGWorld->portRect.right-StringWidth(str) >> 1, 10); |
DrawString(str); |
SetGWorld(savePort, saveGDevice); |
Q3Marker_SetBitmap(gGameFPSMarker, &gGameFPSMarkerData.bitmap); |
} |
// Update the velocity marker |
if (gGameVelocityVisible) |
{ |
TQ3Vector3D velocity; |
Drone_GetVelocity(gGameSelfDrone, &velocity); |
sprintf((char*) str, "x%.1f", gGameFramesPerSecond); |
str[0] = strlen((char*) str) - 1; |
GetGWorld(&savePort, &saveGDevice); |
SetGWorld(gGameVelocityGWorld, NULL); |
EraseRect(&gGameVelocityGWorld->portRect); |
MoveTo(gGameVelocityGWorld->portRect.left+gGameVelocityGWorld->portRect.right-StringWidth(str) >> 1, 10); |
DrawString(str); |
SetGWorld(savePort, saveGDevice); |
Q3Marker_SetBitmap(gGameVelocityMarker, &gGameVelocityMarkerData.bitmap); |
} |
// Get and process state-based inputs |
SelfDrone_Roll(gGameSelfDrone, Input_GetRoll()*gGameInterval*ROLL_RATE); |
SelfDrone_Pitch(gGameSelfDrone, Input_GetPitch()*gGameInterval*PITCH_RATE); |
SelfDrone_Yaw(gGameSelfDrone, Input_GetYaw()*gGameInterval*YAW_RATE); |
if (gInertialDampersOn) SelfDrone_DampVelocity(gGameSelfDrone, DAMPING_PERCENT); |
SelfDrone_Thrust(gGameSelfDrone, Input_GetThrottle()*gGameInterval*ENGINE_RATING); |
if (gFiringOn) |
{ |
UInt32 ticks = TickCount(); |
if (gLastShotTicks + TICKS_PER_AUTOFIRE_SHOT < ticks) |
{ |
Drone_Fire(gGameSelfDrone); |
gLastShotTicks = ticks; |
} |
} |
// Get and process input events |
ok = true; |
do |
{ |
switch (Input_GetEvent()) |
{ |
case kInputEvent_None: |
// No more input events to process |
ok = false; |
break; |
case kInputEvent_Fire_On: |
// Fire button pressed down |
Drone_Fire(gGameSelfDrone); |
gLastShotTicks = TickCount(); |
gFiringOn = true; |
break; |
case kInputEvent_Fire_Off: |
// Fire button released |
gFiringOn = false; |
break; |
case kInputEvent_InertialDampers_On: |
// Inertial Dampers button down |
SelfDrone_DampVelocity(gGameSelfDrone, DAMPING_PERCENT); |
gInertialDampersOn = true; |
break; |
case kInputEvent_InertialDampers_Off: |
// Inertial Dampers button released |
gInertialDampersOn = false; |
break; |
case kInputEvent_InstantStop: |
// Set Velocity to zero |
SelfDrone_InstantStop(gGameSelfDrone); |
break; |
case kInputEvent_ShowHUD: |
// Toggle HMD display |
gGameHUDVisible = !gGameHUDVisible; |
break; |
case kInputEvent_ShowFPS: |
// Toggle FPS display |
gGameFPSVisible = !gGameFPSVisible; |
break; |
case kInputEvent_ShowThrottle: |
// Toggle throttle display |
gGameThrottleVisible = !gGameThrottleVisible; |
break; |
case kInputEvent_ShowVelocity: |
// Toggle velocity display |
gGameVelocityVisible = !gGameVelocityVisible; |
break; |
case kInputEvent_Pause: |
// Toggle paused/play state |
switch (gGameState) |
{ |
case kGameState_Playing: |
Game_SetState(kGameState_Paused); |
break; |
case kGameState_Paused: |
Game_SetState(kGameState_Playing); |
break; |
case kGameState_Stopped: |
// do nothing |
break; |
} |
break; |
} |
} |
while (ok); |
// Move all the drones, mark any to be deleted |
for (drone = Drone_Next(NULL); drone != NULL; drone = Drone_Next(drone)) |
{ |
Drone_Move(drone); |
} |
// Delete the marked drones |
drone = Drone_Next(NULL); |
while (drone != NULL) |
{ |
next = Drone_Next(drone); |
if (Drone_GetMark(drone)) |
{ |
Drone_Dispose(drone); |
} |
drone = next; |
} |
// Move the viewer to follow the self drone |
Drone_GetPosition(gGameSelfDrone, &position); |
Drone_GetDirection(gGameSelfDrone, &direction); |
Drone_GetUp(gGameSelfDrone, &up); |
Display_SetViewerPosition(&position, &direction, &up); |
if (gSoundOn) |
{ |
// Move the listener to follow the self drone |
Drone_GetMatrix(gGameSelfDrone, &matrix); |
SSpListener_SetTransform(Sound_GetListener(), &matrix); |
// Change the localized sounds |
for (drone = Drone_Next(NULL); drone != NULL; drone = Drone_Next(drone)) |
{ |
Drone_UpdateSound(drone); |
} |
} |
// Time to retire? |
// NOTE: This is not the best way to determine if the game is done. We |
// should probably ask each drone if it thinks the game should continue. |
// Only AutoDrones would respond yes. Then we'd "or" the results. |
count = 0; |
for (drone = Drone_Next(NULL); drone != NULL; drone = Drone_Next(drone)) |
{ |
count += 1; |
} |
if (count == 1) |
{ |
// Only the autodrone is left -- time to quit |
Game_SetState(kGameState_Stopped); |
} |
} |
/* ============================================================================= |
* Game_Submit (external) |
* |
* Submits all the 3D geometry of the game. |
* ========================================================================== */ |
void Game_Submit( |
TQ3ViewObject inView) |
{ |
TDroneObject drone; |
TQ3Point3D position; |
TQ3Vector3D direction; |
TQ3Vector3D velocity; |
TQ3Vector3D up; |
TQ3Vector3D v; |
TQ3Point3D markerPosition; |
assert(inView != NULL); |
// Submit the drones |
for (drone = Drone_Next(NULL); drone != NULL; drone = Drone_Next(drone)) |
{ |
Drone_Submit(drone, gGameHUDVisible, inView); |
} |
// Get information about the camera position |
Drone_GetPosition(gGameSelfDrone, &position); |
Drone_GetDirection(gGameSelfDrone, &direction); |
Drone_GetUp(gGameSelfDrone, &up); |
Drone_GetVelocity(gGameSelfDrone, &velocity); |
// Submit the spacejunk |
Space_Submit(inView, &position, &direction); |
// Submit the FPS marker |
if (gGameFPSVisible) |
{ |
Q3Point3D_Vector3D_Add(&position, &direction, &markerPosition); |
Q3Vector3D_Scale(&up, -0.6, &v); |
Q3Point3D_Vector3D_Add(&markerPosition, &v, &markerPosition); |
Q3Marker_SetPosition(gGameFPSMarker, &markerPosition); |
Q3Object_Submit(gGameFPSMarker, inView); |
} |
// Submit the velocity marker |
if (gGameVelocityVisible) |
{ |
Q3Point3D_Vector3D_Add(&position, &direction, &markerPosition); |
Q3Vector3D_Scale(&up, -0.8, &v); |
Q3Point3D_Vector3D_Add(&markerPosition, &v, &markerPosition); |
Q3Marker_SetPosition(gGameVelocityMarker, &markerPosition); |
Q3Object_Submit(gGameVelocityMarker, inView); |
} |
// Submit the crosshairs |
if (gGameHUDVisible) |
{ |
#if HUD_SHOWS_VELOCITY_VECTOR |
Q3Vector3D_Normalize(&velocity, &velocity); |
Q3Point3D_Vector3D_Add(&position, &velocity, &markerPosition); |
#else |
Q3Point3D_Vector3D_Add(&position, &direction, &markerPosition); |
#endif |
Q3Marker_SetPosition(gGameCrossHairs, &markerPosition); |
Q3Object_Submit(gGameCrossHairs, inView); |
} |
} |
/* ============================================================================= |
* Game_Silence (external) |
* |
* Stops all sounds. |
* ========================================================================== */ |
void Game_Silence( |
void) |
{ |
TDroneObject drone; |
for (drone = Drone_Next(NULL); drone != NULL; drone = Drone_Next(drone)) |
{ |
Drone_Silence(drone); |
} |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-10-14