はじめに(2016-7-3)
はじめまして。
プログラマでない自分が無謀にも 「オンライン3Dネットワーク11VS11対戦サッカーゲーム」 を作りたくなり、
活動休止を繰り返しつつ10年ほど経過した今もまったくできそうにない状況です・・・。
自分には無理かもしれないと思いながらも「2ちゃんねる」の「ゲーム製作技術」掲示板のスレでなんとか続けてきました。
今は、「1人でゲームが作れるように修行します。2 」というスレでSGGKという固定ハンドルネームで活動しています。
書きこみは年に数回程度で、休止で数年書かない時もあるかもしれません。
アドレスは以下を参照してください。
http://echo.2ch.net/test/read.cgi/gamedev/1272901469/l50
今、使用している環境は、
コンパイラは、Visual Studio Express 2013 for Windows Desktop
ライブラリは、DXライブラリ
です。
ピンポンゲームの製作(2016-7-3)
しばらく活動を休止して、プログラミングを忘れてしまっていた。
10年近く経過しても結局思ったようには進まずで、
薦められたこともあって、ピンポンのゲームをなんとか頑張って作ってみました。
ゲームのルールは、先に2点を取った方が勝ちです。
イメージ画像は、右側のものです。
ソースは、下にあるのが全てです。
実行ファイルは以下アドレスのアップローダーから入手可能にしてあります。圧縮ファイルなので、解凍してください。
uploader.jp というサイトでレンタルして、
「自作ファイルアップローダー」という名前にしてます。
http://ux.getuploader.com/1357897/
ファイル名は、pong_20160703.zip
ダウンロード用パスは、game
です。
//=========================================
//======= PONG ============================
//======= 2016.6.18にやり直し開始 =========
//======= 2016.7.02 完成 ==================
//=========================================
#include <math.h>
#include <DxLib.h>
#define FIELD_LINE_WIDTH 8 /*フィールドの線幅*/
#define FIELD_MARGIN_UP 20 /*フィールドの上側ラインと画面上端の間隔*/
#define FIELD_SIZE_H 424 /*フィールドサイズ縦幅 480-(20+8)x2 */
#define WINDOW_SIZE_X 640/*ウィンドウ横幅*/
#define WINDOW_SIZE_Y 480/*ウィンドウ縦幅*/
#define RACKET_SIZE_X 10/*ラケット横幅*/
#define RACKET_SIZE_Y 40/*ラケット縦幅*/
#define RACKET_SIZE_05X RACKET_SIZE_X/2 /*ラケット横幅の1/2*/
#define RACKET_SIZE_05Y RACKET_SIZE_Y/2 /*ラケット縦幅の1/2*/
//1P:画面向かって右側、2P:左側
#define RACKET1P_POS_X WINDOW_SIZE_X-25 /*ラケット1P中心部X座標*/
#define RACKET1P_POS_Y WINDOW_SIZE_Y/2 /*ラケット1P中心部Y座標*/
#define RACKET2P_POS_X 25 /*ラケット2P中心部X座標*/
#define RACKET2P_POS_Y WINDOW_SIZE_Y/2 /*ラケット2P中心部Y座標*/
#define RACKET_SPEED 180 /*ラケット速度 単位は、ドット/s だと思う*/
#define BALL_SIZE_XY 10/*ボール縦横幅*/
#define BALL_SIZE_05XY BALL_SIZE_XY/2 /*ボール縦横幅*/
#define BALL_SPEED 140 /*ボール速度 単位は、ドット/s だと思う*/
#define BALL_INI_ANGLE 40 /*ボール初期射出角度*/
#define BALL_START_TIME 2000 /*ボール出現から射出までの待ち時間、単位はms(ミリ秒)*/
#define SCORE_MAX 2 /*勝利条件(最高得点)*/
#define PI 3.1415926f
//グローバル変数
//時間計測用変数
int g_lasttime = 0; //直前の計測時間
float g_frametime = 0; //1ループにかかった時間
int g_timerstart; //タイマー用変数
enum GameState{ //ゲーム状態
GAME_TITLE, GAME_MAIN,
GAME_CLEAR, GAME_OVER,
GAME_SCOREUP
};
GameState g_gamestate = GAME_TITLE;//ゲーム初期状態
GameState g_gamestate_before = GAME_TITLE;//ゲーム初期状態(現在の状態の直前の状態の保存用)
//ラケット初期値
typedef struct racket{
float x;
float y;
}RACKET;
RACKET g_racket_1P = { RACKET1P_POS_X, RACKET1P_POS_Y };
RACKET g_racket_2P = { RACKET2P_POS_X, RACKET2P_POS_Y };
//ボール位置座標初期状態
typedef struct ball{
float x;
float y;
}BALL;
BALL g_ball = { WINDOW_SIZE_X / 2, WINDOW_SIZE_Y / 2 };
//ボール速度(速度というよりは、速度xフレームタイム=1フレーム当たりの移動距離が、x,yに格納されている。
typedef struct ball_v{
float x;
float y;
}BALL_V;
BALL_V g_ball_v = { 0, 0 };//初期化は、DrawGameMain()内で行う。
//ボール速度初期化確認フラグ
enum BallIni_V_State{ //ゲーム状態
BallIniNotEnd,//初期化をしていない状態
BallIniEnd //初期化完了済み状態
};
enum BallIni_V_State g_ballini_v_state = BallIniNotEnd;
//得点初期状態
typedef struct score{
float x;//得点の表示x座標
float y;//得点の表示y座標
int number;//得点
}SCORE;
SCORE g_score_1P = { WINDOW_SIZE_X / 2 - 90, FIELD_MARGIN_UP + 30, 0 };
SCORE g_score_2P = { WINDOW_SIZE_X / 2 + 30, FIELD_MARGIN_UP + 30, 0 };
//フォント
int g_smallfont; //小サイズフォントハンドル
int g_middlefont; //中サイズフォントハンドル
int g_largefont; //大サイズフォントハンドル
//関数プロトタイプ宣言
void DrawGameTitle();
void DrawGameMain();
void DrawGameClear();
void DrawGameOver();
void IniGameMain();
void DrawCharacter();
void DrawField();
void DrawRacket();
int WINAPI WinMain(HINSTANCE h1, HINSTANCE hP, LPSTR lpC, int nC){
//ウィンドウモードにする
ChangeWindowMode(TRUE);
//ウィンドウサイズを変更する
SetGraphMode(WINDOW_SIZE_X, WINDOW_SIZE_Y, 32);
//DXライブラリ初期化
if (DxLib_Init() == -1) return -1;
//画像を読み込み
g_smallfont = CreateFontToHandle("メイリオ", 16, -1, DX_FONTTYPE_ANTIALIASING);
g_middlefont = CreateFontToHandle("メイリオ", 30, -1, DX_FONTTYPE_ANTIALIASING);
g_largefont = CreateFontToHandle("メイリオ", 90, -1, DX_FONTTYPE_ANTIALIASING);
SetDrawScreen(DX_SCREEN_BACK);//描画対象を「裏画面」にする。この処理を書き忘れないように!!
//現在時刻をg_lasttimeに記録
//これが無い場合、ゲームループに入った一回目の時間計算が、
//g_lasttime=0で計算されるのでゲーム1ループ時間とはならない。
g_lasttime = GetNowCount();
//ここからゲーム部分のループ処理に入る。
while (ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0){
//1ループにかかった時間を計測
int curtime = GetNowCount();
g_frametime = (float)(curtime - g_lasttime) / 1000.0f;
g_lasttime = curtime;//このg_lasttimeの値は以下のswitch文、ScreenFlip();終了後に↑の行で使われる。
ClearDrawScreen();
//画面描画関数に切り替え
switch (g_gamestate)
{
case GAME_TITLE:
DrawGameTitle();
break;
case GAME_MAIN:
DrawGameMain();
break;
case GAME_CLEAR:
DrawGameClear();
break;
case GAME_OVER:
DrawGameOver();
break;
default:
break;
}
ScreenFlip();
}
//待機
//WaitKey();
//DXライブラリの終了処理
DxLib_End();
return 0;
}
//タイトル画面描画
void DrawGameTitle(){
//文字描画
DrawCharacter();
//キーをチェックして画面を切り替え
int key = GetJoypadInputState(DX_INPUT_KEY_PAD1);
if (key & PAD_INPUT_A) {//Zキーが押された場合
g_gamestate = GAME_MAIN;
g_gamestate_before = GAME_TITLE;//遷移先で初期化関数を1回だけ動かすのに必要な設定
g_timerstart = g_lasttime;//タイマーセット(ボール発射を待たせる時間を計算するための基準をここで設定する。
}
//Cキー入力によるゲーム終了および終了関数への遷移
if (key & PAD_INPUT_C){//Cキー
g_gamestate = GAME_OVER;
}
}
void DrawGameMain(){
IniGameMain();
DrawCharacter();
DrawField();
DrawRacket();//ラケットは結局、メイン関数の中で書いた。
//Cキー入力による途中でのゲーム終了および終了関数への遷移
int key = GetJoypadInputState(DX_INPUT_KEY_PAD1);
if (key & PAD_INPUT_C){//Cキー
g_gamestate = GAME_OVER;
}
//ラケット描画
float mv = RACKET_SPEED * g_frametime;//180dot/sの速度か?
//1P
if (key & PAD_INPUT_UP){
if ((g_racket_1P.y - RACKET_SIZE_05Y) < (FIELD_MARGIN_UP + FIELD_LINE_WIDTH)){
g_racket_1P.y = FIELD_MARGIN_UP + FIELD_LINE_WIDTH + RACKET_SIZE_05Y;
}
else{
g_racket_1P.y -= mv;
}
}
if (key & PAD_INPUT_DOWN){
if ((g_racket_1P.y + RACKET_SIZE_05Y) > (FIELD_MARGIN_UP + FIELD_LINE_WIDTH + FIELD_SIZE_H)){
g_racket_1P.y = FIELD_MARGIN_UP + FIELD_LINE_WIDTH + FIELD_SIZE_H - RACKET_SIZE_05Y;
}
else{
g_racket_1P.y += mv;
}
}
//2P
if (key & PAD_INPUT_Y){//Sキー
if ((g_racket_2P.y - RACKET_SIZE_05Y) < (FIELD_MARGIN_UP + FIELD_LINE_WIDTH)){
g_racket_2P.y = FIELD_MARGIN_UP + FIELD_LINE_WIDTH + RACKET_SIZE_05Y;
}
else{
g_racket_2P.y -= mv;
}
}
if (key & PAD_INPUT_B){//Xキー
if ((g_racket_2P.y + RACKET_SIZE_05Y) > (FIELD_MARGIN_UP + FIELD_LINE_WIDTH + FIELD_SIZE_H)){
g_racket_2P.y = FIELD_MARGIN_UP + FIELD_LINE_WIDTH + FIELD_SIZE_H - RACKET_SIZE_05Y;
}
else{
g_racket_2P.y += mv;
}
}
//ラケット描画
DrawBox(g_racket_1P.x - RACKET_SIZE_05X, g_racket_1P.y - RACKET_SIZE_05Y,
g_racket_1P.x + RACKET_SIZE_05X, g_racket_1P.y + RACKET_SIZE_05Y, GetColor(255, 255, 255), TRUE);
DrawBox(g_racket_2P.x - RACKET_SIZE_05X, g_racket_2P.y - RACKET_SIZE_05Y,
g_racket_2P.x + RACKET_SIZE_05X, g_racket_2P.y + RACKET_SIZE_05Y, GetColor(255, 255, 255), TRUE);
//ボール
float mv2 = BALL_SPEED * g_frametime;
//ボール描画用の座標値を計算(この後の当たり判定で「当たり」の場合はボール位置とボール速度の再計算が必要。
//初期速度は右上30°の方向に飛ぶ
if (g_ballini_v_state == BallIniNotEnd){
g_ball_v.x = mv2 * cos(PI / 180.0*BALL_INI_ANGLE);
g_ball_v.y = -1.0*mv2 * sin(PI / 180.0 * BALL_INI_ANGLE);
g_ballini_v_state = BallIniEnd;//この設定をしなければ、
//毎回ループを通る時に速度がこのブロック文内で初期化されてしまう。
//タイトルとクリアの画面からメインに遷移する時は、ループでここに来た1回目は速度初期化が必要なので、
//g_ballini_v_state = BallIniNotEnd;をIniGameMain()内で実行している。
}
//ボール位置(候補)計算
g_ball.x += g_ball_v.x;//この位置で問題ないかを以下で計算する。
g_ball.y += g_ball_v.y;//この位置で問題ないかを以下で計算する。
//ボール当たり判定
//画面上下のラインとの衝突判定
//上ライン
if ((g_ball.y - BALL_SIZE_05XY) < (FIELD_MARGIN_UP + FIELD_LINE_WIDTH)){
//上ラインを越えたら、y方向に越えた分を折返す。
g_ball.y = 2 * (FIELD_MARGIN_UP + FIELD_LINE_WIDTH) - (g_ball.y - BALL_SIZE_05XY);
g_ball_v.y = -1.0*g_ball_v.y;
}
//下ライン
if ((g_ball.y + BALL_SIZE_05XY) > (FIELD_MARGIN_UP + FIELD_LINE_WIDTH + FIELD_SIZE_H)){
//下ラインを越えたら、y方向に越えた分を折返す。
g_ball.y = 2 * (FIELD_MARGIN_UP + FIELD_LINE_WIDTH + FIELD_SIZE_H) - (g_ball.y + BALL_SIZE_05XY);
g_ball_v.y = -1.0*g_ball_v.y;
}
//ラケットとの当たり判定
//@ボールが画面外に出たか
//@-1P側(画面右)判定
if ((g_ball.x - BALL_SIZE_05XY) > WINDOW_SIZE_X){
g_score_1P.number += 1;//ボールが画面外に出たので得点加算
if (g_score_1P.number == SCORE_MAX){//加算した得点がSCORE_MAX点に達していたらゲームクリア
g_gamestate = GAME_CLEAR;
}
else{
//g_gamestate = GAME_SCOREUP; ←ここでカウントUPしてるので、この状態の為の関数は用意されていない。
g_gamestate_before = GAME_SCOREUP;//←初期化関数を1度だけ実行するために必要な設定。
g_timerstart = g_lasttime;//タイマーセット
}
//ボールは画面外に出たことになるので、ボール座標の更新は行わない。
//@-2P側(画面左)判定
}else if ((g_ball.x + BALL_SIZE_05XY) < 0){
g_score_2P.number += 1;//ボールが画面外に出たので得点加算
if (g_score_2P.number == SCORE_MAX){//加算した得点がSCORE_MAX点に達していたらゲームクリア
g_gamestate = GAME_CLEAR;
}
else{
//g_gamestate = GAME_SCOREUP; ←ここでカウントUPしてるので、この状態の為の関数は用意されていない。
g_gamestate_before = GAME_SCOREUP;//←初期化関数を1度だけ実行するために必要な設定。
g_timerstart = g_lasttime;//タイマーセット
}
//ボールは画面外に出たことになるので、ボール座標の更新は行わない。
//Aボールがラケットに当たらない事が確実になったか判定
}else if ((g_ball.x - BALL_SIZE_05XY) > (g_racket_1P.x + RACKET_SIZE_05X)){
//A-1P側(画面右)判定
g_ball.x = g_ball.x;//変更しない
g_ball.y = g_ball.y;//変更しない
}else if ((g_ball.x + BALL_SIZE_05XY) < (g_racket_2P.x - RACKET_SIZE_05X)){
//A-2P側(画面左)判定
g_ball.x = g_ball.x;//変更しない
g_ball.y = g_ball.y;//変更しない
//Bボールが明らかにラケットの前面に当たった場合の判定
//B-1P側(画面右)判定
//ボールがラケットの前面ラインのY方向上下幅に一部分でもおさまっているか
}
else if (((g_ball.y - BALL_SIZE_05XY) >= (g_racket_1P.y - RACKET_SIZE_05Y - BALL_SIZE_XY))
&& ((g_ball.y + BALL_SIZE_05XY) <= (g_racket_1P.y + RACKET_SIZE_05Y + BALL_SIZE_XY))){
//上記の条件を満たしたうえで、ボールがラケットの前面ラインに「またがる」状態か
if (((g_ball.x + BALL_SIZE_05XY) > (g_racket_1P.x - RACKET_SIZE_05X))
&& ((g_ball.x - BALL_SIZE_05XY) < (g_racket_1P.x - RACKET_SIZE_05X))){
g_ball.x = 2 * (g_racket_1P.x - RACKET_SIZE_05X) - (g_ball.x + BALL_SIZE_05XY);
g_ball_v.x = -1.0*g_ball_v.x;
}
//B-2P側(画面左)判定
//ボールがラケットの前面ラインのY方向上下幅に一部分でもおさまっているか
}
else if (((g_ball.y - BALL_SIZE_05XY) >= (g_racket_2P.y - RACKET_SIZE_05Y - BALL_SIZE_XY))
&& ((g_ball.y + BALL_SIZE_05XY) <= (g_racket_2P.y + RACKET_SIZE_05Y + BALL_SIZE_XY))){
//ボールがラケットの前面ラインに「またがる」状態か
if (((g_ball.x - BALL_SIZE_05XY) < (g_racket_2P.x + RACKET_SIZE_05X))
&& ((g_ball.x + BALL_SIZE_05XY) > (g_racket_2P.x + RACKET_SIZE_05X))){
g_ball.x = 2 * (g_racket_2P.x + RACKET_SIZE_05X) - (g_ball.x - BALL_SIZE_05XY);
g_ball_v.x = -1.0*g_ball_v.x;
}
}
//ボール(決定位置)描画
DrawBox(g_ball.x - BALL_SIZE_05XY, g_ball.y - BALL_SIZE_05XY,
g_ball.x + BALL_SIZE_05XY, g_ball.y + BALL_SIZE_05XY, GetColor(255, 0, 255), TRUE);
}
void DrawGameClear(){
//文字描画
DrawCharacter();
//キーをチェックして画面を切り替え
int key = GetJoypadInputState(DX_INPUT_KEY_PAD1);
if (key & PAD_INPUT_A) {//Zキーが押された
g_gamestate = GAME_MAIN;
g_gamestate_before = GAME_CLEAR;//遷移先で初期化関数を1回だけ動かすのに必要な設定
g_timerstart = g_lasttime;//タイマーセット
}else if(key & PAD_INPUT_C){
g_gamestate = GAME_OVER;
}
}
void DrawGameOver(){
//文字描画
DrawCharacter();//ESCで抜ける処理はWinMainのwhileループを抜ける条件として、そこに書かれている。
}
void IniGameMain(){
if ((g_gamestate_before == GAME_TITLE) || (g_gamestate_before == GAME_CLEAR)){
g_racket_1P = { RACKET1P_POS_X, RACKET1P_POS_Y };
g_racket_2P = { RACKET2P_POS_X, RACKET2P_POS_Y };
g_ball = { WINDOW_SIZE_X / 2, WINDOW_SIZE_Y / 2 };
//ボール速度は、ボール位置計算の最初のところで1回のみ行っている。
g_ballini_v_state = BallIniNotEnd;//DrawGameMain()でボール位置を計算する為の初期速度を
//1回だけ計算させるのに必要な設定値
//ゲームの開始時に得点の初期化が必要
//SCORE g_score_1P = { WINDOW_SIZE_X / 2 - 90, FIELD_MARGIN_UP + 30, 0 };
//↑こちらの書き方だと、クリア画面からここに遷移しても得点が0にならず、クリア画面になる前の点数が残っていた。
g_score_1P = { WINDOW_SIZE_X / 2 - 90, FIELD_MARGIN_UP + 30, 0 };
//SCORE g_score_2P = { WINDOW_SIZE_X / 2 + 10, FIELD_MARGIN_UP + 30, 0 };
//↑こちらの書き方だと、クリア画面からここに遷移しても得点が0にならず、クリア画面になる前の点数が残っていた。
g_score_2P = { WINDOW_SIZE_X / 2 + 10, FIELD_MARGIN_UP + 30, 0 };
}
else if (g_gamestate_before == GAME_SCOREUP){//←この状態セットは、ボールが画面外に出た判定の直後の行で設定されている。
g_racket_1P = { RACKET1P_POS_X, RACKET1P_POS_Y };
g_racket_2P = { RACKET2P_POS_X, RACKET2P_POS_Y };
g_ball = { WINDOW_SIZE_X / 2, WINDOW_SIZE_Y / 2 };
//ボール速度は、ボール位置計算の最初のところで1回のみ行っている。
g_ballini_v_state = BallIniNotEnd;//DrawGameMain()でボール位置を計算する為の初期速度を
//1回だけ計算させるのに必要な設定値
}
if ((g_lasttime - g_timerstart) >= BALL_START_TIME){//2秒後にボールが動くようにするためのタイマー。
//それまでは↑の初期化を繰り返し続ける。
g_gamestate_before = GAME_MAIN;//上記初期化をこの関数内で1回実行したので、
//それ以降IniGameMain()が行われないようにしている。
//↓このように書いてもコンパイラエラーにならず、気付くのに時間がかかった。
//ラケットが上下に振動して動かなかったバグが出た。
//GameState g_gamestate_before = GAME_MAIN;
}
}
void DrawCharacter(){
switch (g_gamestate)
{
case GAME_TITLE:
DrawStringToHandle(200, 100, "PONG",
GetColor(255, 255, 255), g_largefont);
DrawStringToHandle(100, 280, "Zキーでゲームスタート",
GetColor(255, 255, 255), g_middlefont);
DrawStringToHandle(100, 320, "Cキーでゲーム終了",
GetColor(255, 255, 255), g_middlefont);
DrawStringToHandle(100, 360, "1P:↑キー、↓キーで上下移動",
GetColor(255, 255, 255), g_middlefont);
DrawStringToHandle(100, 400, "2P:Sキー、Xキーで上下移動",
GetColor(255, 255, 255), g_middlefont);
break;
case GAME_MAIN:
DrawFormatStringToHandle(g_score_1P.x, g_score_1P.y,
GetColor(255, 255, 255), g_largefont, "%d", g_score_1P.number);
DrawFormatStringToHandle(g_score_2P.x, g_score_2P.y,
GetColor(255, 255, 255), g_largefont, "%d", g_score_2P.number);
DrawStringToHandle(260, 462, "Cキーでゲーム終了",
GetColor(255, 255, 255), g_smallfont);
DrawStringToHandle(450, 462, "1P ↑キー:上、↓キー:下",
GetColor(255, 255, 255), g_smallfont);//1Pのみならx座標620にする。
DrawStringToHandle(0, 462, "2P Sキー:上、Xキー:下",
GetColor(255, 255, 255), g_smallfont);
break;
case GAME_CLEAR:
DrawStringToHandle(20, 100, "GAME CLEAR",
GetColor(255, 255, 255), g_largefont);
if (g_score_1P.number > g_score_2P.number){
DrawStringToHandle(120, 200, "1P WIN!",
GetColor(255, 255, 255), g_largefont);
}
else{
DrawStringToHandle(120, 200, "2P WIN!",
GetColor(255, 255, 255), g_largefont);
}
DrawStringToHandle(100, 300, "Zキーでゲームコンティニュー",
GetColor(255, 255, 255), g_middlefont);
DrawStringToHandle(100, 340, "Cキーでゲーム終了",
GetColor(255, 255, 255), g_middlefont);
break;
case GAME_OVER:
DrawStringToHandle(50, 100, "GAME OVER",
GetColor(255, 255, 255), g_largefont);
DrawStringToHandle(10, 280, "ESCキーを押してこの画面を閉じてください",
GetColor(255, 255, 255), g_middlefont);
break;
default:
break;
}
}
void DrawField(){
int p1_x = 0;
int p1_y = FIELD_MARGIN_UP;
int p2_x = WINDOW_SIZE_X;
int p2_y = FIELD_MARGIN_UP + FIELD_LINE_WIDTH;
DrawBox(p1_x, p1_y, p2_x, p2_y, GetColor(255, 255, 255), TRUE);/*上側ライン*/
DrawBox(p1_x, p1_y + FIELD_SIZE_H + FIELD_LINE_WIDTH, p2_x, p2_y + FIELD_SIZE_H + FIELD_LINE_WIDTH,
GetColor(255, 255, 255), TRUE);/*下側ライン*/
//中央の点線縦ライン
p1_x = WINDOW_SIZE_X / 2 - FIELD_LINE_WIDTH/2;
p1_y = FIELD_MARGIN_UP + FIELD_LINE_WIDTH + 22;//22は最初の隙間。それ以降の隙間は20。最後の隙間は再び22で中央ラインを描く。
p2_x = WINDOW_SIZE_X / 2 + FIELD_LINE_WIDTH/2;
p2_y = p1_y + 20;//20は中央ラインの点線1つの高さ。
for (int i = 0; i < 10; i++){
DrawBox(p1_x, p1_y + 40 * i, p2_x, p2_y + 40 * i, GetColor(255, 255, 255), TRUE);
}
}
void DrawRacket(){
//ラケットは結局、メイン関数の中で書いた。
}
以上