営業業務用電卓アプリケーションを作成しました。

従来店舗で使用していた電卓は左図のようなものでポイントを表示する欄がなく、実際の支払い金額と実質での金額が分かりづらかった問題がありました。そこで自作でその問題を解決することができるアプリケーションを作成いたしました。

改善点(左図が従来汎用電卓、右図が作成した専用電卓)
・商品それぞれの基本ポイント倍率が異なる為、それぞれで設定可能に
・種別を「ポイント」にすることでポイントが倍率から定数として入力可能に。それにより特定のキャンペーンによる「OOポイントプレゼント!」などに対応。
・合計金額と合計ポイントでそれぞれ表示することで視覚的に分かりやすく。

以下ソースコードになります。

#ifdef __APPLE__
    /* Mac */
    #define GL_SILENCE_DEPRECATION
    #include <GLUT/glut.h>
#else
    /* Windows / Linux */
    #include "GL/glut.h"
#endif

#include <math.h>
#include <stdlib.h>
#include <stdio.h>

/* Window size */
const int WIDTH = 1280;
const int HEIGHT = 720;
int winW = 1280;
int winH = 720;

/* Base Physics Constants */
const float BRAKE = 0.05f;
const float BASE_STEER = 1.4f;
const float FRICTION = 0.003f;
const float ENGINE_BRAKE = 0.008f;

/* Track Settings */
#define NUM_TRACK_POINTS 1600
const float ROAD_WIDTH = 50.0f;

/* Track Node Structure */
typedef struct {
    float x;
    float z;
    float nx;
    float nz;
} TrackNode;

TrackNode trackNodes[NUM_TRACK_POINTS];

/* Car State */
float carX = 0.0f;
float carY = 0.0f;
float carZ = 0.0f;
float carAngle = 0.0f;
float carSpeed = 0.0f;

/* Transmission State */
int currentGear = 0;
float currentRPM = 800.0f;

/* Key States */
int keyW = 0;
int keyA = 0;
int keyS = 0;
int keyD = 0;

/* PI constant for drawing */
const float PI = 3.14159265f;

/* MT Physics Helpers */
float getGearMaxSpeed(int gear) {
    switch (gear) {
        case -1: return 0.5f;  /* Reverse */
        case 1:  return 0.5f;  /* 1st (~50km/h) */
        case 2:  return 0.9f;  /* 2nd (~90km/h) */
        case 3:  return 1.3f;  /* 3rd (~130km/h) */
        case 4:  return 1.7f;  /* 4th (~170km/h) */
        case 5:  return 2.1f;  /* 5th (~210km/h) */
        case 6:  return 2.4f;  /* 6th (~240km/h) */
        default: return 0.0f;  /* Neutral */
    }
}

float getGearAccel(int gear) {
    switch (gear) {
        case -1: return 0.015f;
        case 1:  return 0.025f;
        case 2:  return 0.018f;
        case 3:  return 0.014f;
        case 4:  return 0.010f;
        case 5:  return 0.008f;
        case 6:  return 0.006f;
        default: return 0.0f;
    }
}

/* Track Generation Function */
void initTrack(void) {
    int i, next;
    float t, r, dx, dz, len;

    for (i = 0; i < NUM_TRACK_POINTS; i++) {
        t = (float)i / (float)NUM_TRACK_POINTS * 2.0f * PI;
        r = 1300.0f + 500.0f * (float)sin(4.0f * t) + 350.0f * (float)cos(7.0f * t) + 150.0f * (float)sin(10.0f * t);
        trackNodes[i].x = r * (float)sin(t);
        trackNodes[i].z = r * (float)cos(t);
    }

    for (i = 0; i < NUM_TRACK_POINTS; i++) {
        next = (i + 1) % NUM_TRACK_POINTS;
        dx = trackNodes[next].x - trackNodes[i].x;
        dz = trackNodes[next].z - trackNodes[i].z;
        len = (float)sqrt(dx * dx + dz * dz);
        trackNodes[i].nx = dz / len;
        trackNodes[i].nz = -dx / len;
    }

    carX = trackNodes[0].x;
    carZ = trackNodes[0].z;
    carAngle = (float)atan2(trackNodes[1].x - trackNodes[0].x,
                            trackNodes[1].z - trackNodes[0].z) * 180.0f / PI;
}

/* Off-track detection */
int isOffTrack(float px, float pz) {
    int i, next;
    float minDist = 999999.0f;
    float x1, z1, x2, z2, l2, t, projX, projZ, dist;

    for (i = 0; i < NUM_TRACK_POINTS; i++) {
        next = (i + 1) % NUM_TRACK_POINTS;
        x1 = trackNodes[i].x;
        z1 = trackNodes[i].z;
        x2 = trackNodes[next].x;
        z2 = trackNodes[next].z;

        l2 = (x2 - x1) * (x2 - x1) + (z2 - z1) * (z2 - z1);
        t = 0.0f;

        if (l2 > 0.0f) {
            t = ((px - x1) * (x2 - x1) + (pz - z1) * (z2 - z1)) / l2;
            if (t < 0.0f) t = 0.0f;
            if (t > 1.0f) t = 1.0f;
        }

        projX = x1 + t * (x2 - x1);
        projZ = z1 + t * (z2 - z1);
        dist = (float)sqrt((px - projX) * (px - projX) + (pz - projZ) * (pz - projZ));

        if (dist < minDist) {
            minDist = dist;
        }
    }

    return (minDist <= ROAD_WIDTH / 2.0f) ? 0 : 1;
}

/* 2D Text Drawing Helper */
void drawString(float x, float y, const char* str) {
    const char* c;
    glRasterPos2f(x, y);
    for (c = str; *c != '\0'; c++) {
        glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, *c);
    }
}

/* 2D Text Drawing Helper (Small font) */
void drawStringSmall(float x, float y, const char* str) {
    const char* c;
    glRasterPos2f(x, y);
    for (c = str; *c != '\0'; c++) {
        glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, *c);
    }
}

/* Map a value to an angle for the dials */
float mapValueToAngle(float val, float minVal, float maxVal, float startAngle, float endAngle) {
    float ratio = (val - minVal) / (maxVal - minVal);
    if (ratio < 0.0f) ratio = 0.0f;
    if (ratio > 1.0f) ratio = 1.0f;
    return startAngle + ratio * (endAngle - startAngle);
}

/* Central HUD Drawing (Sporty Blue Neon Dashboard) */
void drawHUD(void) {
    int i;
    /* 画面中央下部に配置 */
    float midX = (float)winW / 2.0f;
    float cy = 130.0f;
    /* メーター間の距離を大幅に離す (被り対策) */
    float tachoCx = midX - 280.0f;
    float speedoCx = midX + 280.0f;
    float radius = 110.0f;
    
    /* 角度設定: 左下(225度)から右下(-45度)へ広がるメーター */
    float startA = 225.0f * PI / 180.0f;
    float endA   = -45.0f * PI / 180.0f;

    /* 実速度 (km/h) = 内部スピード * 100 */
    float displayKmh = (float)fabs(carSpeed) * 100.0f;
    char textBuf[32];

    /* 2D Projection Setup */
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    gluOrtho2D(0.0, (GLdouble)winW, 0.0, (GLdouble)winH);
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();

    glDisable(GL_LIGHTING);
    glDisable(GL_DEPTH_TEST);

    /* --- メーター背景共通処理 --- */
    float centers[2] = {tachoCx, speedoCx};
    for (int d = 0; d < 2; d++) {
        /* メーター盤面(ダークブルー〜黒) */
        glColor3f(0.02f, 0.05f, 0.1f);
        glBegin(GL_TRIANGLE_FAN);
        glVertex2f(centers[d], cy);
        for (i = 0; i <= 270; i += 10) {
            float a = startA + (i / 270.0f) * (endA - startA);
            glVertex2f(centers[d] + (float)cos(a) * radius, cy + (float)sin(a) * radius);
        }
        glEnd();

        /* ネオンブルーの太い外枠 */
        glColor3f(0.1f, 0.4f, 1.0f);
        glLineWidth(4.0f);
        glBegin(GL_LINE_STRIP);
        for (i = 0; i <= 270; i += 5) {
            float a = startA + (i / 270.0f) * (endA - startA);
            glVertex2f(centers[d] + (float)cos(a) * radius, cy + (float)sin(a) * radius);
        }
        glEnd();
        
        /* 少し内側の細いリング */
        glColor3f(0.4f, 0.7f, 1.0f);
        glLineWidth(1.0f);
        glBegin(GL_LINE_STRIP);
        for (i = 0; i <= 270; i += 5) {
            float a = startA + (i / 270.0f) * (endA - startA);
            glVertex2f(centers[d] + (float)cos(a) * (radius - 5.0f), cy + (float)sin(a) * (radius - 5.0f));
        }
        glEnd();
    }

    /* =========================================
       左側:タコメーター (0 - 8000 RPM)
       ========================================= */
    /* 目盛りと数字 */
    for (i = 0; i <= 8; i++) {
        float val = i * 1000.0f;
        float a = mapValueToAngle(val, 0.0f, 8000.0f, startA, endA);
        
        /* 7000以上はレッドゾーン */
        if (i >= 7) glColor3f(1.0f, 0.1f, 0.1f);
        else glColor3f(1.0f, 1.0f, 1.0f);

        /* 太い目盛り */
        glLineWidth(3.0f);
        glBegin(GL_LINES);
        glVertex2f(tachoCx + (float)cos(a) * (radius - 5.0f), cy + (float)sin(a) * (radius - 5.0f));
        glVertex2f(tachoCx + (float)cos(a) * (radius - 15.0f), cy + (float)sin(a) * (radius - 15.0f));
        glEnd();

        /* 数字の描画(円の内側に沿って配置) */
        float nx = tachoCx + (float)cos(a) * (radius - 30.0f) - 5.0f;
        float ny = cy + (float)sin(a) * (radius - 30.0f) - 5.0f;
        sprintf(textBuf, "%d", i);
        drawString(nx, ny, textBuf);
    }
    
    /* 間の細かい目盛り */
    for (i = 0; i <= 80; i++) {
        float val = i * 100.0f;
        float a = mapValueToAngle(val, 0.0f, 8000.0f, startA, endA);
        if (val >= 7000.0f) glColor3f(1.0f, 0.1f, 0.1f);
        else glColor3f(0.5f, 0.7f, 1.0f);
        glLineWidth(1.0f);
        glBegin(GL_LINES);
        glVertex2f(tachoCx + (float)cos(a) * (radius - 5.0f), cy + (float)sin(a) * (radius - 5.0f));
        glVertex2f(tachoCx + (float)cos(a) * (radius - 10.0f), cy + (float)sin(a) * (radius - 10.0f));
        glEnd();
    }

    /* タコメーターの針(ネオンブルー) */
    float tachoAngle = mapValueToAngle(currentRPM, 0.0f, 8000.0f, startA, endA);
    glColor3f(0.2f, 0.6f, 1.0f);
    glLineWidth(4.0f);
    glBegin(GL_LINES);
    glVertex2f(tachoCx, cy);
    glVertex2f(tachoCx + (float)cos(tachoAngle) * (radius - 18.0f), cy + (float)sin(tachoAngle) * (radius - 18.0f));
    glEnd();

    glColor3f(1.0f, 1.0f, 1.0f);
    drawStringSmall(tachoCx - 20.0f, cy - 30.0f, "x1000");
    drawStringSmall(tachoCx - 15.0f, cy - 45.0f, "RPM");


    /* =========================================
       右側:スピードメーター (0 - 220 km/h)
       ========================================= */
    /* 目盛りと数字 (20km/h刻み) */
    for (i = 0; i <= 220; i += 20) {
        float a = mapValueToAngle((float)i, 0.0f, 220.0f, startA, endA);
        
        glColor3f(1.0f, 1.0f, 1.0f);
        glLineWidth(3.0f);
        glBegin(GL_LINES);
        glVertex2f(speedoCx + (float)cos(a) * (radius - 5.0f), cy + (float)sin(a) * (radius - 5.0f));
        glVertex2f(speedoCx + (float)cos(a) * (radius - 15.0f), cy + (float)sin(a) * (radius - 15.0f));
        glEnd();

        /* 数字の配置 */
        float nx = speedoCx + (float)cos(a) * (radius - 35.0f) - 10.0f;
        float ny = cy + (float)sin(a) * (radius - 35.0f) - 5.0f;
        sprintf(textBuf, "%d", i);
        if (i == 0 || i == 220 || i % 40 == 0) {
            drawStringSmall(nx, ny, textBuf);
        }
    }

    /* 間の細かい目盛り (10km/h刻み) */
    for (i = 0; i <= 220; i += 10) {
        float a = mapValueToAngle((float)i, 0.0f, 220.0f, startA, endA);
        glColor3f(0.5f, 0.7f, 1.0f);
        glLineWidth(1.0f);
        glBegin(GL_LINES);
        glVertex2f(speedoCx + (float)cos(a) * (radius - 5.0f), cy + (float)sin(a) * (radius - 5.0f));
        glVertex2f(speedoCx + (float)cos(a) * (radius - 10.0f), cy + (float)sin(a) * (radius - 10.0f));
        glEnd();
    }

    /* スピードメーターの針(ネオンブルー) */
    float speedAngle = mapValueToAngle(displayKmh, 0.0f, 220.0f, startA, endA);
    glColor3f(0.2f, 0.6f, 1.0f);
    glLineWidth(4.0f);
    glBegin(GL_LINES);
    glVertex2f(speedoCx, cy);
    glVertex2f(speedoCx + (float)cos(speedAngle) * (radius - 18.0f), cy + (float)sin(speedAngle) * (radius - 18.0f));
    glEnd();

    glColor3f(1.0f, 1.0f, 1.0f);
    sprintf(textBuf, "%3.0f", displayKmh);
    drawString(speedoCx - 15.0f, cy - 30.0f, textBuf);
    drawStringSmall(speedoCx - 15.0f, cy - 45.0f, "km/h"); /* ここが途切れていた箇所です */

    /* 針の中心のピン */
    glColor3f(0.0f, 0.0f, 0.0f);
    glPointSize(16.0f);
    glBegin(GL_POINTS);
    glVertex2f(tachoCx, cy);
    glVertex2f(speedoCx, cy);
    glEnd();
    glColor3f(0.2f, 0.5f, 1.0f);
    glPointSize(8.0f);
    glBegin(GL_POINTS);
    glVertex2f(tachoCx, cy);
    glVertex2f(speedoCx, cy);
    glEnd();

    /* =========================================
       中央:ギアインジケーター & シフトランプ
       ========================================= */
    /* ギア表示 */
    glColor3f(1.0f, 1.0f, 1.0f);
    if (currentGear == -1) sprintf(textBuf, "R");
    else if (currentGear == 0) sprintf(textBuf, "N");
    else sprintf(textBuf, "%d", currentGear);
    
    drawString(midX - 5.0f, cy - 20.0f, textBuf);
    drawStringSmall(midX - 15.0f, cy - 40.0f, "GEAR");

    /* レブリミット警告ランプ (7000RPM以上で点灯) */
    if (currentRPM >= 7000.0f) {
        glColor3f(1.0f, 0.0f, 0.0f); /* 赤 */
        glBegin(GL_QUADS);
        glVertex2f(midX - 30.0f, cy + 40.0f);
        glVertex2f(midX + 30.0f, cy + 40.0f);
        glVertex2f(midX + 30.0f, cy + 50.0f);
        glVertex2f(midX - 30.0f, cy + 50.0f);
        glEnd();
        glColor3f(1.0f, 1.0f, 1.0f);
        drawStringSmall(midX - 25.0f, cy + 60.0f, "SHIFT UP!");
    }

    /* Restore 3D settings */
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
}

/* Initialization */
void init(void) {
    glClearColor(0.4f, 0.6f, 0.9f, 1.0f);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_NORMALIZE);

    initTrack();
}

/* Draw Car */
void drawCar(void) {
    int i;
    float wheelPoses[4][2] = {
        { 0.65f,  0.7f },
        {-0.65f,  0.7f },
        { 0.65f, -0.7f },
        {-0.65f, -0.7f }
    };

    glPushMatrix();
    glTranslatef(carX, carY + 0.25f, carZ);
    glRotatef(carAngle, 0.0f, 1.0f, 0.0f);

    glColor3f(0.85f, 0.05f, 0.05f);
    glPushMatrix();
    glScalef(1.2f, 0.3f, 2.4f);
    glutSolidCube(1.0f);
    glPopMatrix();

    glPushMatrix();
    glTranslatef(0.0f, 0.3f, -0.2f);
    glScalef(1.0f, 0.35f, 1.2f);
    glutSolidCube(1.0f);
    glPopMatrix();

    glPushMatrix();
    glTranslatef(0.0f, 0.2f, -1.15f);
    glScalef(0.9f, 0.1f, 0.1f);
    glutSolidCube(1.0f);
    glPopMatrix();

    glColor3f(0.1f, 0.1f, 0.1f);
    glPushMatrix();
    glTranslatef(0.0f, 0.35f, 0.45f);
    glScalef(0.9f, 0.25f, 0.1f);
    glutSolidCube(1.0f);
    glPopMatrix();
    glPushMatrix();
    glTranslatef(0.0f, 0.35f, -0.85f);
    glScalef(0.9f, 0.25f, 0.1f);
    glutSolidCube(1.0f);
    glPopMatrix();

    glColor3f(0.05f, 0.05f, 0.05f);
    glPushMatrix();
    glTranslatef(0.0f, -0.05f, 1.21f);
    glScalef(0.8f, 0.15f, 0.01f);
    glutSolidCube(1.0f);
    glPopMatrix();

    glColor3f(0.1f, 0.1f, 0.1f);
    for (i = 0; i < 4; i++) {
        glPushMatrix();
        glTranslatef(wheelPoses[i][0], -0.1f, wheelPoses[i][1]);
        glScalef(0.2f, 0.35f, 0.35f);
        glutSolidSphere(1.0, 16, 16);
        glPopMatrix();
    }

    glPopMatrix();
}

/* Draw Track */
void drawTrack(void) {
    int i, idx;
    float nx, nz, nx2, nz2;

    glNormal3f(0.0f, 1.0f, 0.0f);

    glColor3f(0.2f, 0.6f, 0.2f);
    glBegin(GL_QUADS);
    glVertex3f(-5000.0f, -0.1f,  5000.0f);
    glVertex3f(-5000.0f, -0.1f, -5000.0f);
    glVertex3f( 5000.0f, -0.1f, -5000.0f);
    glVertex3f( 5000.0f, -0.1f,  5000.0f);
    glEnd();

    glColor3f(0.3f, 0.3f, 0.3f);
    glBegin(GL_QUAD_STRIP);
    for (i = 0; i <= NUM_TRACK_POINTS; i++) {
        idx = i % NUM_TRACK_POINTS;
        nx = trackNodes[idx].nx * (ROAD_WIDTH / 2.0f);
        nz = trackNodes[idx].nz * (ROAD_WIDTH / 2.0f);
        glVertex3f(trackNodes[idx].x - nx, 0.0f, trackNodes[idx].z - nz);
        glVertex3f(trackNodes[idx].x + nx, 0.0f, trackNodes[idx].z + nz);
    }
    glEnd();

    glColor3f(1.0f, 1.0f, 1.0f);
    glLineWidth(2.0f);
    glBegin(GL_LINES);
    for (i = 0; i < NUM_TRACK_POINTS; i++) {
        if (i % 2 == 0) {
            int next = (i + 1) % NUM_TRACK_POINTS;
            glVertex3f(trackNodes[i].x, 0.02f, trackNodes[i].z);
            glVertex3f(trackNodes[next].x, 0.02f, trackNodes[next].z);
        }
    }
    glEnd();

    nx2 = trackNodes[0].nx * (ROAD_WIDTH / 2.0f);
    nz2 = trackNodes[0].nz * (ROAD_WIDTH / 2.0f);
    
    glColor3f(1.0f, 0.2f, 0.2f);
    glLineWidth(5.0f);
    glBegin(GL_LINES);
    glVertex3f(trackNodes[0].x - nx2, 0.03f, trackNodes[0].z - nz2);
    glVertex3f(trackNodes[0].x + nx2, 0.03f, trackNodes[0].z + nz2);
    glEnd();
}

/* Display Callback */
void display(void) {
    float rad = carAngle * PI / 180.0f;
    
    float camX = carX - (float)sin(rad) * 9.0f;
    float camY = carY + 3.5f;
    float camZ = carZ - (float)cos(rad) * 9.0f;

    float lookX = carX + (float)sin(rad) * 4.0f;
    float lookY = carY + 0.5f;
    float lookZ = carZ + (float)cos(rad) * 4.0f;

    GLfloat lightPos[] = { 0.5f, 1.0f, 0.5f, 0.0f };

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    gluLookAt(camX, camY, camZ,  lookX, lookY, lookZ,  0.0f, 1.0f, 0.0f);
    glLightfv(GL_LIGHT0, GL_POSITION, lightPos);

    drawTrack();
    drawCar();
    drawHUD(); /* 中央固定のスポーティメーター */

    glutSwapBuffers();
}

/* Physics Update with MT Logic */
void update(int value) {
    float rad, currentSteer, speedRatio, turnDir, targetRPM;
    float gearMax = getGearMaxSpeed(currentGear);
    float gearAcc = getGearAccel(currentGear);

    /* Throttle & Brake Processing */
    if (keyW) {
        if (currentGear > 0) {
            if (carSpeed < gearMax) {
                carSpeed += gearAcc;
            } else {
                /* レブリミッターによる燃料カット(レッドゾーン頭打ち) */
                carSpeed -= ENGINE_BRAKE * 3.0f;
            }
        } else if (currentGear == -1) {
            if (carSpeed > -gearMax) {
                carSpeed -= gearAcc;
            }
        }
    } else if (keyS) {
        if (carSpeed > 0.01f) carSpeed -= BRAKE;
        else if (carSpeed < -0.01f) carSpeed += BRAKE;
        else carSpeed = 0.0f;
    } else {
        if (carSpeed > 0.01f) carSpeed -= ENGINE_BRAKE;
        else if (carSpeed < -0.01f) carSpeed += ENGINE_BRAKE;
        else carSpeed = 0.0f;
    }

    if (currentGear > 0 && carSpeed > gearMax + 0.05f) {
        carSpeed -= ENGINE_BRAKE * 4.0f;
    }

    /* RPM Calculation (7000でレッドゾーン突入, 8000で完全に頭打ち) */
    if (currentGear == 0) {
        if (keyW) targetRPM = 7800.0f;
        else targetRPM = 800.0f;
        currentRPM += (targetRPM - currentRPM) * 0.1f;
    } else {
        if (gearMax > 0.0f) {
            /* 現在のギア比での回転数を計算 */
            float ratio = (float)fabs(carSpeed) / gearMax;
            targetRPM = 800.0f + ratio * 6800.0f; /* 最大速度で約7600RPM(レッドゾーン)になるように */
            
            /* シフトダウン時のオーバーレブ防止ハードリミット */
            if (targetRPM > 8000.0f) targetRPM = 8000.0f;
        } else {
            targetRPM = 800.0f;
        }
        /* 針の動き(シフトアップ時にストンと落ちるレスポンスを調整) */
        currentRPM += (targetRPM - currentRPM) * 0.25f;
    }

    /* Steering */
    if (fabs(carSpeed) > 0.01f) {
        turnDir = (carSpeed > 0) ? 1.0f : -1.0f;
        speedRatio = (float)fabs(carSpeed) / 2.4f; /* 最高速基準 */
        currentSteer = BASE_STEER * (1.2f - speedRatio * 0.7f); /* 高速域でハンドルを重く */
        
        if (keyA) carAngle += currentSteer * turnDir;
        if (keyD) carAngle -= currentSteer * turnDir;
    }

    /* Update Position */
    rad = carAngle * PI / 180.0f;
    carX += carSpeed * (float)sin(rad);
    carZ += carSpeed * (float)cos(rad);

    /* Off-Track Penalty */
    if (isOffTrack(carX, carZ)) {
        if (fabs(carSpeed) > 0.5f) {
            if (carSpeed > 0) carSpeed -= BRAKE * 1.5f;
            else carSpeed += BRAKE * 1.5f;
        }
    }

    glutPostRedisplay();
    glutTimerFunc(16, update, 0);
}

/* Reshape: ウィンドウサイズが変わっても中央に配置し続ける */
void reshape(int w, int h) {
    winW = w;
    winH = h;
    
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, (double)w / (double)h, 1.0, 3000.0);
    glMatrixMode(GL_MODELVIEW);
}

/* Standard Keyboard Input */
void keyboardDown(unsigned char key, int x, int y) {
    switch (key) {
        case 'w': case 'W': keyW = 1; break;
        case 'a': case 'A': keyA = 1; break;
        case 's': case 'S': keyS = 1; break;
        case 'd': case 'D': keyD = 1; break;
        case 27: exit(0); break;
    }
}

void keyboardUp(unsigned char key, int x, int y) {
    switch (key) {
        case 'w': case 'W': keyW = 0; break;
        case 'a': case 'A': keyA = 0; break;
        case 's': case 'S': keyS = 0; break;
        case 'd': case 'D': keyD = 0; break;
    }
}

/* MT Shifting */
void specialDown(int key, int x, int y) {
    if (key == GLUT_KEY_UP) {
        if (currentGear < 6) currentGear++;
    } else if (key == GLUT_KEY_DOWN) {
        if (currentGear > -1) currentGear--;
    }
}

/* Main */
int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
    glutInitWindowSize(WIDTH, HEIGHT);
    glutCreateWindow("OpenGL GR86 Race Game - Sporty HUD");

    init();

    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    
    glutKeyboardFunc(keyboardDown);
    glutKeyboardUpFunc(keyboardUp);
    glutSpecialFunc(specialDown);
    
    glutTimerFunc(16, update, 0);

    glutMainLoop();
    return 0;
}

コメント