Weird bugs…
I got the sudden urge to hand-code Pong. It's not finished; I have to implement sides(?), losing, and another paddle. There are a couple bugs I can't figure out, though.
1). When you press the left/right arrow key, and then quickly press another key, the paddle continues as though you are holding down the first key.
2). When you press the left/right arrow key, and then press the other key without waiting for a second or so, the paddle jerks(instead of moving) when you press either of the keys again.
P.S: Go easy on my code; this is my first try.
-LG
1). When you press the left/right arrow key, and then quickly press another key, the paddle continues as though you are holding down the first key.
2). When you press the left/right arrow key, and then press the other key without waiting for a second or so, the paddle jerks(instead of moving) when you press either of the keys again.
Code:
//
// LGLView.m
// LinkPong
//
// Created by Lincoln Green on 7/20/08.
//
#import "LGLView.h"
#import <GLUT/glut.h>
@implementation LGLView
- (void)awakeFromNib{
paddlePos1 = 0;
paddlePos2 = 0;
ballPosX = 0;
ballPosY = 0;
paddle1Y = -2.0;
xDir = 0; //left NSEvent
yDir = 0; //Down
leftBounds = [self bounds].size.width/100 * - 1.0;
rightBounds = [self bounds].size.width/100;
topBounds = [self bounds].size.height/100;
NSLog(@"%f", leftBounds);
NSLog(@"%f", rightBounds);
gameTimer = [[NSTimer scheduledTimerWithTimeInterval:0.05
target:self
selector:@selector(moveBall)
userInfo:nil
repeats:YES] retain];
}
- (void)prepare{
NSLog(@"prepare");
NSOpenGLContext *glContext = [self openGLContext];
[glContext makeCurrentContext];
glShadeModel(GL_SMOOTH);
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
GLfloat ambient[] = {0.2, 0.2, 0.2, 1.0};
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
GLfloat diffuse[] = {1.0, 1.0, 1.0, 1.0};
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
glEnable(GL_LIGHT0);
GLfloat mat[] = {0.1, 0.1, 0.78, 1.0};
glMaterialfv(GL_FRONT, GL_AMBIENT, mat);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat);
}
- (void)reshape{
NSLog(@"reshape");
NSRect baseRect = [self convertRectToBase:[self bounds]];
glViewport(0, 0, baseRect.size.width, baseRect.size.height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0, baseRect.size.width/baseRect.size.height, 0.2, 7);
}
- (void)drawRect:(NSRect)r{
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glPushMatrix();
glColorf(1.0, 1.0, 1.0);
glBegin(GL_QUADS);
glVertex3f(paddlePos1 - 0.5, -2.0f, -5.0f);
glVertex3f(paddlePos1 + 0.5, -2.0f, -5.0f);
glVertex3f(paddlePos1 + 0.5, -2.2f, -5.0f);
glVertex3f(paddlePos1 - 0.5, -2.2f, -5.0f);
glEnd();
glBegin(GL_QUADS);
glVertex3f(ballPosX - 0.25f, ballPosY + 0.25f, -5.0f);
glVertex3f(ballPosX - 0.25f, ballPosY - 0.25f, -5.0f);
glVertex3f(ballPosX + 0.25f, ballPosY - 0.25f, -5.0f);
glVertex3f(ballPosX + 0.25f, ballPosY + 0.25f, -5.0f);
glEnd();
glFinish();
}
- (void)keyDown:(NSEvent *)theEvent{
moveInterval = 0.2;
NSString *characters;
characters = [theEvent characters];
unichar character;
character = [characters characterAtIndex: 0];
if (character == NSRightArrowFunctionKey) {
if((paddlePos1 + 0.4) < ([self bounds].size.width/100)){
if([theEvent isARepeat]){
paddlePos1 += 0.1;
NSLog(@"Right arrow");
NSLog(@"%f", [self bounds].size.width);
NSLog(@"%f", paddlePos1);
NSLog(@"moved");
} else {
paddleTimer = [[NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:@selector(movePaddleRight)
userInfo:nil
repeats:YES] retain];
}
}
} else if (character == NSLeftArrowFunctionKey) {
if((paddlePos1 - 0.4) > (([self bounds].size.width / 100) * -1)){
if ([theEvent isARepeat]){
paddlePos1 -= 0.1;
NSLog(@"Left arrow");
NSLog(@"%f", [self bounds].size.width);
NSLog(@"%f", paddlePos1);
NSLog(@"moved");
} else {
paddleTimer = [[NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:@selector(movePaddleLeft)
userInfo:nil
repeats:YES] retain];
}
}
}
[self setNeedsDisplay:YES];
}
- (BOOL)acceptsFirstResponder{
return YES;
}
- (void)moveBall{
if(xDir == 0 /*left*/){
if (ballPosX - 0.25 > leftBounds){
ballPosX -= 0.1;
}
else{
xDir = 1; //right
}
}
if (xDir == 1 /*right*/){
if (ballPosX + 0.25 < rightBounds){
ballPosX += 0.1;
}
else{
xDir = 0;
}
}
if (yDir == 0 /*down*/){
if (ballPosY - 0.25 > paddle1Y){
ballPosY -= 0.1;
} else{
if(ballPosX + 0.25 > paddlePos1 - 0.5 && ballPosX - 0.25 < paddlePos1 + 0.5){
yDir = 1; //up
} else {
ballPosY -= 0.1;
}
}
} else {
if (ballPosY + 0.25 < topBounds){
ballPosY += 0.1;
}
else{
yDir = 0;
}
}
[self setNeedsDisplay:YES];
}
- (void)movePaddleLeft{
NSLog(@"moveInterval = %f", moveInterval);
if (moveInterval > 0){
paddlePos1 -= moveInterval;
moveInterval -= 0.05;
[self setNeedsDisplay:YES];
return;
}
[paddleTimer invalidate];
[paddleTimer release];
paddleTimer = nil;
NSLog(@"moveInterval = %f", moveInterval);
}
- (void)movePaddleRight{
NSLog(@"moveInterval = %f", moveInterval);
if (moveInterval > 0){
paddlePos1 += (moveInterval * 1);
moveInterval -= 0.05;
[self setNeedsDisplay:YES];
return;
}
[paddleTimer invalidate];
[paddleTimer release];
paddleTimer = nil;
NSLog(@"moveInterval = %f", moveInterval);
}
@endCode:
#import <Cocoa/Cocoa.h>
@interface LGLView : NSOpenGLView {
float paddlePos1;
float paddlePos2;
float ballPosX;
float ballPosY;
int yDir;
int xDir;
float moveInterval;
float leftBounds;
float rightBounds;
float topBounds;
float paddle1Y;
NSTimer *gameTimer;
NSTimer *paddleTimer;
}
- (void)moveBall;
- (void)movePaddleLeft;
- (void)movePaddleRight;
@endP.S: Go easy on my code; this is my first try.

-LG
To be honest, I didn't fully inspect your code, but I can see right off the bat that there is a better way of doing things. What you should do is keep an array of booleans (or individual variables if there aren't many) for each key. When you receive a KeyDown event, you set the boolean to true, and when you receive a KeyUp event, you set it to false. Here is some psuedo code:
Code:
bool keys[256];
- (void)keyDown:(NSEvent *)theEvent {
unsigned short keyCode = [theEvent keyCode];
keys[keyCode] = true;
}
- (void)keyUp:(NSEvent *)theEvent {
unsigned short keyCode = [theEvent keyCode];
keys[keyCode] = false;
}
- (void)movePaddle() {
// dt is the interval you passed when you created the scheduled timer (i.e. 0.05 in this case)
if(keyCode[kKeyboardLeftArrow])
paddlePos1 -= PADDLE_SPEED*dt;
if(keyCode[kKeyboardRightArrow])
paddlePos1 += PADDLE_SPEED*dt;
// Do boundary check here
}
enum kKeyCode {
kKeyboardA = 0x00,
kKeyboardB = 0x0b,
kKeyboardC = 0x08,
kKeyboardD = 0x02,
kKeyboardE = 0x0e,
kKeyboardF = 0x03,
kKeyboardG = 0x05,
kKeyboardH = 0x04,
kKeyboardI = 0x22,
kKeyboardJ = 0x26,
kKeyboardK = 0x28,
kKeyboardL = 0x25,
kKeyboardM = 0x2e,
kKeyboardN = 0x2d,
kKeyboardO = 0x1f,
kKeyboardP = 0x23,
kKeyboardQ = 0x0c,
kKeyboardR = 0x0f,
kKeyboardS = 0x01,
kKeyboardT = 0x11,
kKeyboardU = 0x20,
kKeyboardV = 0x09,
kKeyboardW = 0x0d,
kKeyboardX = 0x07,
kKeyboardY = 0x10,
kKeyboardZ = 0x06,
kKeyboard1 = 0x12,
kKeyboard2 = 0x13,
kKeyboard3 = 0x14,
kKeyboard4 = 0x15,
kKeyboard5 = 0x17,
kKeyboard6 = 0x16,
kKeyboard7 = 0x1a,
kKeyboard8 = 0x1c,
kKeyboard9 = 0x19,
kKeyboard0 = 0x1d,
kKeyboardSpace = 0x31,
kKeyboardLeftArrow = 0x7b,
kKeyboardRightArrow = 0x7c,
kKeyboardUpArrow = 0x7e,
kKeyboardDownArrow = 0x7d,
kKeyboardEscape = 0x35
};
And to add to that, you should have one update timer, not one update timer for every event that's created!
The reason you may be getting some lag there is you are firing your paddleTimer with a delay. Also when two keys are pressed you will invalidate the other timer when your move function is called.
If your paddleTimer != nil, then you are overwriting the value of it with your second call or canceling the value of it when you complete a move in movePaddleRight or movePaddleLeft. The best way to handle this is to ignore a separate timer for the movement and just handle it through the game loop.
As Nevada mentioned you should set boolean flags when you have a key down then remove them when you have a key up. Then inside your game loop you test for those flags if they are true the key is currently down and you can move. If you don't want to accept key holding you can also remove the flag here and it won't be registered again till another hit.
If your paddleTimer != nil, then you are overwriting the value of it with your second call or canceling the value of it when you complete a move in movePaddleRight or movePaddleLeft. The best way to handle this is to ignore a separate timer for the movement and just handle it through the game loop.
As Nevada mentioned you should set boolean flags when you have a key down then remove them when you have a key up. Then inside your game loop you test for those flags if they are true the key is currently down and you can move. If you don't want to accept key holding you can also remove the flag here and it won't be registered again till another hit.
Going with what everyone else is talking about, here is an example of how you could do it (if you actually try it, hit space to launch the ball):
LGLView2.h
LGLView2.m
If you want other keys than what are listed as special cases in the enum, just use something like if (key['p']) to see if the p key is down.
LGLView2.h
Code:
#import <Cocoa/Cocoa.h>
typedef enum
{
ENTER = 3,
TAB = 9,
RETURN = 13,
ESC = 27,
SPACE = 32,
DEL = 127,
UP_ARROW, DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW,
F1, F2, F3, F4, F5, F6, F7, F8,
HOME, END, PG_UP, PG_DN, CONTROL, OPTION, SHIFT, CAPS_LOCK,
NUM_KEY_CODES
} KeyCode;
@interface LGLView2 : NSOpenGLView {
NSTimer *renderTimer;
float viewWidth;
float viewHeight;
float viewAspect;
double timeLeftOverForUpdate;
double prevFrameTime;
double currTime;
double deltaTime;
double frameTime;
double frameDeltaTime;
double updateRate;
BOOL key[NUM_KEY_CODES];
BOOL keyDown[NUM_KEY_CODES];
BOOL keyUp[NUM_KEY_CODES];
BOOL keyDownQueue[NUM_KEY_CODES];
BOOL keyUpQueue[NUM_KEY_CODES];
float paddlePos1;
float paddlePos2;
float ballPosX;
float ballPosY;
int yDir;
int xDir;
BOOL ballMoving;
BOOL alreadyMissedPaddle1;
}
@endLGLView2.m
Code:
#import "LGLView2.h"
#import <sys/time.h>
#define UPDATE_RATE 110.0
#define MINIMUM_FRAME_RATE 4.0
#define BOUNDS_LEFT -1.0f
#define BOUNDS_RIGHT 1.0f
#define BOUNDS_BOTTOM -1.0f
#define BOUNDS_TOP 1.0f
#define PADDLE_HALF_WIDTH 0.2f
#define PADDLE_HALF_HEIGHT 0.02f
#define PADDLE_SPEED 2.5f
#define PADDLE_VERT_POS 0.85f
#define BALL_RADIUS 0.025f
#define BALL_SPEED 1.0f
@interface LGLView2 (InternalMethods)
- (double)currentTime;
- (void)processKeys:(NSEvent *)theEvent;
// pong methods
- (void)initPong;
- (void)updatePong;
- (void)drawPong;
- (void)moveBall;
@end
@implementation LGLView2
#pragma mark -
#pragma mark GLView Basic Support
- (id)initWithFrame:(NSRect)frameRect
{
// context setup
NSOpenGLPixelFormat *windowedPixelFormat;
NSOpenGLPixelFormatAttribute attribs[] = {
NSOpenGLPFAWindow,
NSOpenGLPFAColorSize, 32,
NSOpenGLPFAAccelerated,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFASingleRenderer,
0 };
windowedPixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
if (windowedPixelFormat == nil)
{
NSLog(@"Unable to create windowed pixel format.");
exit(0);
}
self = [super initWithFrame:frameRect pixelFormat:windowedPixelFormat];
if (self == nil)
{
NSLog(@"Unable to create a windowed OpenGL context.");
exit(0);
}
[windowedPixelFormat release];
// set synch to VBL to eliminate tearing
GLint vblSynch = 1;
[[self openGLContext] setValues:&vblSynch forParameter:NSOpenGLCPSwapInterval];
// setup our fixed deltaTime
deltaTime = 1.0 / UPDATE_RATE;
[self initPong];
// timer setup
renderTimer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:0.0]
interval:0.001
target:self
selector:@selector(renderTimerCallback:)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:renderTimer forMode:NSEventTrackingRunLoopMode];
[[NSRunLoop currentRunLoop] addTimer:renderTimer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] addTimer:renderTimer forMode:NSModalPanelRunLoopMode];
[renderTimer release];
return self;
}
- (void)awakeFromNib
{
NSSize viewBounds;
// for simplicity, we'll make the window a 1 to 1 aspect ratio in size
viewBounds = [self bounds].size;
viewWidth = viewBounds.width;
[[self window] setContentSize:NSMakeSize(viewWidth, viewWidth)];
[[self window] setContentAspectRatio:NSMakeSize(1.0f, 1.0f)];
}
- (BOOL)acceptsFirstResponder
{
return YES;
}
- (void)reshape
{
NSSize viewBounds;
viewBounds = [self bounds].size;
viewWidth = viewBounds.width;
viewHeight = viewBounds.height;
viewAspect = viewWidth / viewHeight;
[[self openGLContext] update];
}
- (void)renderTimerCallback:(NSTimer*)theTimer
{
double timeAllowed, maxTimeAllowed, maxUpdatesPerFrame, tickInterval;
// calculate basic render timing info
currTime = [self currentTime];
frameTime = currTime;
frameDeltaTime = currTime - prevFrameTime;
prevFrameTime = [self currentTime];
// we call update at a fixed interval (UPDATE_RATE), so we need to do some calculations
// to figure out how many times we need to update (or not) each frame, and carry over
// any extra time remaining to be used next frame
tickInterval = deltaTime;
maxUpdatesPerFrame = UPDATE_RATE / MINIMUM_FRAME_RATE;
maxTimeAllowed = maxUpdatesPerFrame * tickInterval;
timeAllowed = frameDeltaTime + timeLeftOverForUpdate;
if (timeAllowed > maxTimeAllowed)
timeAllowed = maxTimeAllowed;
while (timeAllowed > tickInterval)
{
timeAllowed -= tickInterval;
currTime = [self currentTime];
[self update];
}
timeLeftOverForUpdate = timeAllowed;
// lets the OS call drawRect for best window system synchronization
[self display];
}
- (void)update
{
int i;
// clear current input values
for (i = 0; i < NUM_KEY_CODES; i++)
{
keyDown[i] = NO;
keyUp[i] = NO;
}
// transfer input values stored in the input queues since last update, to the current input
for (i = 0; i < NUM_KEY_CODES; i++)
{
keyDown[i] = keyDownQueue[i];
if (keyDown[i])
{
key[i] = YES;
}
keyDownQueue[i] = NO;
keyUp[i] = keyUpQueue[i];
if (keyUp[i])
{
key[i] = NO;
}
keyUpQueue[i] = NO;
}
[self updatePong];
}
- (void)drawRect:(NSRect)rect
{
NSOpenGLContext *currentContext;
currentContext = [self openGLContext];
[currentContext makeCurrentContext];
glViewport(0, 0, viewWidth, viewHeight);
[self drawPong];
[currentContext flushBuffer];
}
- (double)currentTime
{
struct timeval time;
gettimeofday(&time, nil);
return (double)time.tv_sec + (0.000001 * (double)time.tv_usec);
}
- (void)keyDown:(NSEvent *)theEvent
{
[self processKeys:theEvent];
}
- (void)keyUp:(NSEvent *)theEvent
{
[self processKeys:theEvent];
}
- (void)processKeys:(NSEvent *)theEvent
{
NSEventType type;
unsigned int characterIndex, characterCount;
NSString *characters;
unichar c;
int convertedCode;
type = [theEvent type];
characters = [theEvent charactersIgnoringModifiers];
characterCount = [characters length];
for (characterIndex = 0; characterIndex < characterCount; characterIndex++)
{
c = [characters characterAtIndex: characterIndex];
//NSLog(@"%X", c); // useful for keyboard debugging
if (c < 128)
convertedCode = c;
else if (c == NSLeftArrowFunctionKey)
convertedCode = LEFT_ARROW;
else if (c == NSRightArrowFunctionKey)
convertedCode = RIGHT_ARROW;
else if (c == NSUpArrowFunctionKey)
convertedCode = UP_ARROW;
else if (c == NSDownArrowFunctionKey)
convertedCode = DOWN_ARROW;
else if (c == NSF1FunctionKey)
convertedCode = F1;
else if (c == NSF2FunctionKey)
convertedCode = F2;
else if (c == NSF3FunctionKey)
convertedCode = F3;
else if (c == NSF4FunctionKey)
convertedCode = F4;
else if (c == NSF5FunctionKey)
convertedCode = F5;
else if (c == NSF6FunctionKey)
convertedCode = F6;
else if (c == NSF7FunctionKey)
convertedCode = F7;
else if (c == NSF8FunctionKey)
convertedCode = F8;
else if (c == NSHomeFunctionKey)
convertedCode = HOME;
else if (c == NSEndFunctionKey)
convertedCode = END;
else if (c == NSPageUpFunctionKey)
convertedCode = PG_UP;
else if (c == NSPageDownFunctionKey)
convertedCode = PG_DN;
else
break;
if (type == NSKeyDown)
keyDownQueue[convertedCode] = YES;
else
keyUpQueue[convertedCode] = YES;
}
}
#pragma mark -
#pragma mark Pong
- (void)initPong
{
paddlePos1 = 0.0f;
ballPosX = 0.0f;
ballPosY = -PADDLE_VERT_POS + PADDLE_HALF_HEIGHT + BALL_RADIUS;
ballMoving = NO;
yDir = 1.0f;
xDir = 1.0f;
alreadyMissedPaddle1 = NO;
}
- (void)updatePong
{
if (key[LEFT_ARROW])
{
paddlePos1 -= PADDLE_SPEED * deltaTime;
// make sure the paddle doesn't go too far left
if ((paddlePos1 - PADDLE_HALF_WIDTH) < BOUNDS_LEFT)
paddlePos1 = BOUNDS_LEFT + PADDLE_HALF_WIDTH;
}
if (key[RIGHT_ARROW])
{
paddlePos1 += PADDLE_SPEED * deltaTime;
// make sure the paddle doesn't go too far right
if (paddlePos1 > (BOUNDS_RIGHT - PADDLE_HALF_WIDTH))
paddlePos1 = (BOUNDS_RIGHT - PADDLE_HALF_WIDTH);
}
if (!ballMoving && keyDown[SPACE])
ballMoving = YES;
if (ballMoving)
[self moveBall];
else
ballPosX = paddlePos1;
}
- (void)moveBall
{
float penetration;
ballPosX += BALL_SPEED * deltaTime * xDir;
ballPosY += BALL_SPEED * deltaTime * yDir;
// check for collisions with right and left bounds
if ((ballPosX + BALL_RADIUS) > BOUNDS_RIGHT)
{
xDir = -1.0f;
penetration = (ballPosX + BALL_RADIUS) - BOUNDS_RIGHT;
ballPosX -= penetration * 2.0f;
}
if ((ballPosX - BALL_RADIUS) < BOUNDS_LEFT)
{
xDir = 1.0f;
penetration = BOUNDS_LEFT - (ballPosX - BALL_RADIUS);
ballPosX += penetration * 2.0f;
}
// handle paddle1 collisions
if (alreadyMissedPaddle1)
{
if ((ballPosY + BALL_RADIUS) < BOUNDS_BOTTOM)
{
// fell out the bottom of the view so start a new game
ballMoving = NO;
alreadyMissedPaddle1 = NO;
xDir = 1.0f;
yDir = 1.0f;
ballPosX = paddlePos1;
ballPosY = -PADDLE_VERT_POS + PADDLE_HALF_HEIGHT + BALL_RADIUS;
}
}
else if ((yDir == -1.0f) && ((ballPosY - BALL_RADIUS) < (-PADDLE_VERT_POS + PADDLE_HALF_HEIGHT)))
{
// check if direct hit with paddle1
if (!alreadyMissedPaddle1 &&
(ballPosX - BALL_RADIUS) < (paddlePos1 + PADDLE_HALF_WIDTH) &&
(ballPosX + BALL_RADIUS) > (paddlePos1 - PADDLE_HALF_WIDTH))
{
yDir = 1.0f;
penetration = (-PADDLE_VERT_POS + PADDLE_HALF_HEIGHT) - (ballPosY - BALL_RADIUS);
ballPosY += penetration * 2.0f;
}
else
alreadyMissedPaddle1 = YES;
}
// for now, just bounce the ball off the top
if ((ballPosY + BALL_RADIUS) > BOUNDS_TOP)
{
yDir = -1.0f;
penetration = (ballPosY + BALL_RADIUS) - BOUNDS_TOP;
ballPosY -= penetration * 2.0f;
}
}
- (void)drawPong
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// setup projection matrix
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
// matches (BOUNDS_LEFT, BOUNDS_RIGHT, BOUNDS_BOTTOM, BOUNDS_TOP, -1.0, 1.0)
glOrtho(-viewAspect, viewAspect, -1.0, 1.0, -1.0, 1.0);
// setup modelview matrix
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
// begin actual drawing
glColor4f(1.0, 1.0, 1.0, 1.0f);
// draw the first paddle
glPushMatrix();
glTranslatef(paddlePos1, -PADDLE_VERT_POS, 0.0f);
glBegin(GL_QUADS);
glVertex2f(-PADDLE_HALF_WIDTH, -PADDLE_HALF_HEIGHT);
glVertex2f(PADDLE_HALF_WIDTH, -PADDLE_HALF_HEIGHT);
glVertex2f(PADDLE_HALF_WIDTH, PADDLE_HALF_HEIGHT);
glVertex2f(-PADDLE_HALF_WIDTH, PADDLE_HALF_HEIGHT);
glEnd();
glPopMatrix();
// draw the ball
glPushMatrix();
glTranslatef(ballPosX, ballPosY, 0.0f);
glBegin(GL_QUADS);
glVertex2f(-BALL_RADIUS, -BALL_RADIUS);
glVertex2f(BALL_RADIUS, -BALL_RADIUS);
glVertex2f(BALL_RADIUS, BALL_RADIUS);
glVertex2f(-BALL_RADIUS, BALL_RADIUS);
glEnd();
glPopMatrix();
// pop modelview
glPopMatrix();
// pop projection
glMatrixMode(GL_PROJECTION);
glPopMatrix();
}
@endIf you want other keys than what are listed as special cases in the enum, just use something like if (key['p']) to see if the p key is down.
Alright,
Now I'm afraid of replicating what I did with Cocoa and Objective-C(http://www.idevgames.com/forum/showthread.php?t=14758), so if I am asking completely irrelevant questions, give me a red flag and I'll go do my homework before posting again.
Seth: So you're saying I should have a timer that updates every second or so and updates my drawing based on variables?
kodex: OK, that makes sense; thanks.
Nevada: Thanks, I'll try that
AnotherJake: I pasted your code into files named LGLView2, and set my NSOpenGLView in IB, but all that shows is a white square; nothing in the log as well.
I will study it more thoroughly when I get a chance.
Thanks guys, for all the reply's
-LG
Now I'm afraid of replicating what I did with Cocoa and Objective-C(http://www.idevgames.com/forum/showthread.php?t=14758), so if I am asking completely irrelevant questions, give me a red flag and I'll go do my homework before posting again.
Seth: So you're saying I should have a timer that updates every second or so and updates my drawing based on variables?
kodex: OK, that makes sense; thanks.
Nevada: Thanks, I'll try that
AnotherJake: I pasted your code into files named LGLView2, and set my NSOpenGLView in IB, but all that shows is a white square; nothing in the log as well.
I will study it more thoroughly when I get a chance.
Thanks guys, for all the reply's
-LG
Hairball183 Wrote:Seth: So you're saying I should have a timer that updates every second or so and updates my drawing based on variables?
Yes, but unless you want your game running at 1 fps, I suggest you make it run a little more often than that.

Look at how Jake did it.
FreakSoftware Wrote:Yes, but unless you want your game running at 1 fps, I suggest you make it run a little more often than that.
The original pong had a very very slow refresh rate, he might be going for accuracy
kodex Wrote:The original pong had a very very slow refresh rate, he might be going for accuracy

@ Hairball183: That's because you're using an NSOpenGLView in IB, and I'm using a "Custom View". Sorry, I didn't realize that before. You can find the Custom View under Layout Views in the IB Library. It's nearly the same thing as the NSOpenGLView (In fact, it is the superclass), with the only major difference being that you don't specify settings in IB, you have to specify them in code (see initWithFrame). Just replace your NSOpenGLView in IB with a Custom View and set the class and run. It should work fine. The NSOpenGLView in IB used to be downright awful in previous OS X releases so we hardly ever used it, but I've heard it is much improved lately. I haven't tried it myself though.
I feel compelled to add that there is some tricky stuff going on in renderTimerCallback. Here it is again for reference:
You can find a tutorial on what this is all about at ThemsAllTook's site:
http://www.sacredsoftware.net/tutorials/...tion.xhtml
I would say not to worry too much if you can't quite understand how this particular section of code works, because honestly, it took me quite a while to wrap my head around it. But it is a very important technique to decouple your framerate from your update rate. That is, drawing is to be done separately from updating of the positions of the stuff being drawn.
Code:
- (void)renderTimerCallback:(NSTimer*)theTimer
{
double timeAllowed, maxTimeAllowed, maxUpdatesPerFrame, tickInterval;
// calculate basic render timing info
currTime = [self currentTime];
frameTime = currTime;
frameDeltaTime = currTime - prevFrameTime;
prevFrameTime = [self currentTime];
// we call update at a fixed interval (UPDATE_RATE), so we need to do some calculations
// to figure out how many times we need to update (or not) each frame, and carry over
// any extra time remaining to be used next frame
tickInterval = deltaTime;
maxUpdatesPerFrame = UPDATE_RATE / MINIMUM_FRAME_RATE;
maxTimeAllowed = maxUpdatesPerFrame * tickInterval;
timeAllowed = frameDeltaTime + timeLeftOverForUpdate;
if (timeAllowed > maxTimeAllowed)
timeAllowed = maxTimeAllowed;
while (timeAllowed > tickInterval)
{
timeAllowed -= tickInterval;
currTime = [self currentTime];
[self update];
}
timeLeftOverForUpdate = timeAllowed;
// lets the OS call drawRect for best window system synchronization
[self display];
}You can find a tutorial on what this is all about at ThemsAllTook's site:
http://www.sacredsoftware.net/tutorials/...tion.xhtml
I would say not to worry too much if you can't quite understand how this particular section of code works, because honestly, it took me quite a while to wrap my head around it. But it is a very important technique to decouple your framerate from your update rate. That is, drawing is to be done separately from updating of the positions of the stuff being drawn.
FreakSoftware Wrote:Yes, but unless you want your game running at 1 fps, I suggest you make it run a little more often than that.
Look at how Jake did it.
Well, yeah.

kodex Wrote:The original pong had a very very slow refresh rate, he might be going for accuracy

AnotherJake Wrote:
@ Hairball183: That's because you're using an NSOpenGLView in IB, and I'm using a "Custom View". Sorry, I didn't realize that before. You can find the Custom View under Layout Views in the IB Library. It's nearly the same thing as the NSOpenGLView (In fact, it is the superclass), with the only major difference being that you don't specify settings in IB, you have to specify them in code (see initWithFrame). Just replace your NSOpenGLView in IB with a Custom View and set the class and run. It should work fine. The NSOpenGLView in IB used to be downright awful in previous OS X releases so we hardly ever used it, but I've heard it is much improved lately. I haven't tried it myself though.
Ah… My bad.
-LG

