#include <QDOffscreen.h>
#define kBaseResID 128
#define kMoveToFront (WindowPtr)-1L
#define kBallRadius 20
#define kInitialBallXPosition 100
#define kInitialBallYPosition 100
#define kBallXVelocity 2
#define kBallYVelocity 2
#define kBallColor redColor
typedef struct Vec2
{
float x;
float y;
} Vec2;
typedef struct Ball
{
Vec2 position;
Vec2 velocity;
float radius;
} Ball;
// Function prototypes
void ToolBoxInit();
WindowPtr WindowInit();
GWorldPtr OffscreenGraphicsInit(WindowPtr window);
short round(float number);
void MainLoop(WindowPtr window, GWorldPtr offscreenGraphicsWorld);
void DoBallPhysics(Ball* ball, float deltaTicks, WindowPtr window, Rect* ballRect);
void StepBallPhysics(Ball* ball, float deltaTime, Rect* ballRect);
void DrawBall(Rect* ballRect, Rect* rectToClear, GWorldPtr offscreenGraphicsWorld);
void main()
{
WindowPtr window;
GWorldPtr offscreenGraphicsWorld;
ToolBoxInit();
window = WindowInit();
offscreenGraphicsWorld = OffscreenGraphicsInit(window);
MainLoop(window, offscreenGraphicsWorld);
}
void ToolBoxInit()
{
InitGraf(&thePort);
InitFonts();
InitWindows();
InitMenus();
TEInit();
InitDialogs(nil);
InitCursor();
}
WindowPtr WindowInit()
{
WindowPtr window = GetNewWindow(kBaseResID, nil, kMoveToFront);
if(window == nil) // Couldn't load the WIND resource
{
SysBeep(10);
ExitToShell();
}
ShowWindow(window);
SetPort(window);
FillRect(&(window->portRect), black);
return window;
}
GWorldPtr OffscreenGraphicsInit(WindowPtr window)
{
GWorldPtr offscreenGraphicsWorld;
QDErr error = NewGWorld(&offscreenGraphicsWorld, 0, &(window->portRect), nil, nil, 0);
if(error != noErr || offscreenGraphicsWorld == nil) // Couldn't create offscreen graphics world
{
SysBeep(10);
ExitToShell();
}
return offscreenGraphicsWorld;
}
short round(float number)
{
return number < 0 ? (short)(number - 0.5f) : (short)(number + 0.5f);
}
void MainLoop(WindowPtr window, GWorldPtr offscreenGraphicsWorld)
{
Ball ball =
{
{ kInitialBallXPosition, kInitialBallYPosition },
{ kBallXVelocity, kBallYVelocity },
kBallRadius
};
Rect ballRect;
Rect previousBallRect = window->portRect;
long lastLoopTicks = TickCount();
while(!Button())
{
long currentLoopTicks = TickCount();
float deltaTicks = currentLoopTicks - lastLoopTicks;
lastLoopTicks = currentLoopTicks;
DoBallPhysics(&ball, deltaTicks, window, &ballRect);
DrawBall(&ballRect, &previousBallRect, offscreenGraphicsWorld);
previousBallRect = ballRect;
}
}
void DoBallPhysics(Ball* ball, float deltaTicks, WindowPtr window, Rect* ballRect)
{
Vec2 initialBallPosition = ball->position;
float deltaTime = deltaTicks * 0.5f;
StepBallPhysics(ball, deltaTime, ballRect);
if(ballRect->left < window->portRect.left || ballRect->right > window->portRect.right)
{
ball->velocity.x = -ball->velocity.x;
ball->position.x = initialBallPosition.x;
ball->position.y = initialBallPosition.y;
StepBallPhysics(ball, deltaTime, ballRect);
}
if(ballRect->top < window->portRect.top || ballRect->bottom > window->portRect.bottom)
{
ball->velocity.y = -ball->velocity.y;
ball->position.x = initialBallPosition.x;
ball->position.y = initialBallPosition.y;
StepBallPhysics(ball, deltaTime, ballRect);
}
}
void StepBallPhysics(Ball* ball, float deltaTime, Rect* ballRect)
{
ball->position.x += ball->velocity.x * deltaTime;
ball->position.y += ball->velocity.y * deltaTime;
ballRect->top = round(ball->position.y - ball->radius);
ballRect->left = round(ball->position.x - ball->radius);
ballRect->bottom = round(ball->position.y + ball->radius);
ballRect->right = round(ball->position.x + ball->radius);
}
void DrawBall(Rect* ballRect, Rect* rectToClear, GWorldPtr offscreenGraphicsWorld)
{
GWorldPtr onscreenGraphicsWorld;
GDHandle onscreenDevice;
PixMapHandle graphicsWorldPixelMap;
GetGWorld(&onscreenGraphicsWorld, &onscreenDevice);
SetGWorld(offscreenGraphicsWorld, nil);
graphicsWorldPixelMap = GetGWorldPixMap(offscreenGraphicsWorld);
if(!LockPixels(graphicsWorldPixelMap)) // Graphics world buffer has been purged
{
SysBeep(10);
ExitToShell();
}
ForeColor(blackColor);
PaintRect(rectToClear); // Just clear the ball's Rect from the previous frame instead of the whole port
ForeColor(kBallColor);
PaintOval(ballRect);
SetGWorld(onscreenGraphicsWorld, onscreenDevice);
CopyBits(&(((GrafPtr)offscreenGraphicsWorld)->portBits), &(((GrafPtr)onscreenGraphicsWorld)->portBits), &(offscreenGraphicsWorld->portRect), &(onscreenGraphicsWorld->portRect), srcCopy, nil);
if(QDError() != noErr) // Probably means insufficient memory
{
SysBeep(10);
ExitToShell();
}
UnlockPixels(graphicsWorldPixelMap);
}