Weird bugs…

Moderator
Posts: 623
Joined: 2007.09
Post: #1
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.

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);
}
@end

Code:
#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;
@end

P.S: Go easy on my code; this is my first try. Rasp

-LG

- Lincoln Green
http://www.binkworks.com/
Quote this message in a reply
Member
Posts: 100
Joined: 2006.05
Post: #2
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
    };
Quote this message in a reply
⌘-R in Chief
Posts: 1,254
Joined: 2002.05
Post: #3
And to add to that, you should have one update timer, not one update timer for every event that's created!
Quote this message in a reply
Moderator
Posts: 613
Joined: 2004.09
Post: #4
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.
Quote this message in a reply
Moderator
Posts: 3,572
Joined: 2003.06
Post: #5
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
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;
}

@end

LGLView2.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();
}

@end

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.
Quote this message in a reply
Moderator
Posts: 623
Joined: 2007.09
Post: #6
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

- Lincoln Green
http://www.binkworks.com/
Quote this message in a reply
⌘-R in Chief
Posts: 1,254
Joined: 2002.05
Post: #7
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. Smile

Look at how Jake did it.
Quote this message in a reply
Moderator
Posts: 613
Joined: 2004.09
Post: #8
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. Smile

The original pong had a very very slow refresh rate, he might be going for accuracy Rasp
Quote this message in a reply
Moderator
Posts: 3,572
Joined: 2003.06
Post: #9
kodex Wrote:The original pong had a very very slow refresh rate, he might be going for accuracy Rasp

LOL

@ 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.
Quote this message in a reply
Moderator
Posts: 3,572
Joined: 2003.06
Post: #10
I feel compelled to add that there is some tricky stuff going on in renderTimerCallback. Here it is again for reference:
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.
Quote this message in a reply
Moderator
Posts: 623
Joined: 2007.09
Post: #11
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. Smile

Look at how Jake did it.

Well, yeah. Rolleyes

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

LOL
AnotherJake Wrote:LOL

@ 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

- Lincoln Green
http://www.binkworks.com/
Quote this message in a reply
Post Reply