見出し画像

GameMakerでAIと一緒に作る「卓球」

前回はAIと一緒にブラウザで動く「卓球」を作ってみた。

AIと一緒に作ったブラウザ版「卓球」

ダメもとでAIにGameMakerでプログラムできるか聞いてみたら、できるというので、今回は、以前に自力で作った「卓球」を目標に、AIと一緒に、GameMakerで「卓球」を作ってみる。


自力で作った「卓球」

自力で作った卓球

AIはCopilotさん


GamaMakerで「卓球」を作る

早速、お願いしてみる。

卓球ゲームのコードをGamemaker用に書き直して欲しい
Game Maker Studioでの卓球ゲームのコードを書き直すよ。まず、簡単な卓球ゲームを作成するための手順を説明するね:

1. 新しいプロジェクトを作成
2. スプライトの作成:プレイヤー、コンピュータ、ボール、壁用のスプライトを作成する。
3. オブジェクトの作成:スプライトを使用してプレイヤー、コンピュータ、ボールのオブジェクトを作成する。
4. ルームの作成:ゲームのルームを作成し、プレイヤー、コンピュータ、ボールのオブジェクトを配置する。
5. コードの記述:オブジェクトの動きや衝突判定のコードを記述する。

具体的なコード例は以下の通り:

卓球をGameMaker用のコードに書き直す

スプライトの作成

spr_player、spr_computer、spr_ball の3つのスプライトを作成。
spr_playerとspr_computerは10x50ピクセルの長方形、spr_ballは半径10ピクセルの円。

copilot
spriteを用意

以前、自力で作ったボール、パドル、背景を用意。
スプライトを右クリックすると、メニューの中に「既存の物を追加」と出てくるので、そこから追加ができる。
ない場合は、Copilotの言う通りの長方形と円をツールを使って描けばOK。

オブジェクトの作成

obj_player、obj_computer、obj_ball の3つのオブジェクトを作成。

copilot

プレイヤーオブジェクトのコード(obj_player)

Create Event(作成)

// Create Event
x = 20;
y = room_height / 2 - sprite_height / 2;

Step Event(ステップ)

// Step Event
if (keyboard_check(vk_up)) {
    y -= 5;
}
if (keyboard_check(vk_down)) {
    y += 5;
}
y = clamp(y, 0, room_height - sprite_height);

コンピュータオブジェクトのコード(obj_computer)

Create Event(作成)

// Create Event
x = room_width - 30;
y = room_height / 2 - sprite_height / 2;

Step Event(ステップ)

// Step Event
if (obj_ball.y < y + sprite_height / 2) {
    y -= 5;
} else if (obj_ball.y > y + sprite_height / 2) {
    y += 5;
}
y = clamp(y, 0, room_height - sprite_height);

ボールオブジェクトのコード(obj_ball)

Create Event(作成)

// Create Event
x = room_width / 2;
y = room_height / 2;
speed = 5;
direction = choose(0, 180); // 左右どちらかに発射

Step Event(ステップ)

// Step Event
x += lengthdir_x(speed, direction);
y += lengthdir_y(speed, direction);

// 壁の反射
if (y <= 0 || y >= room_height) {
    direction = 360 - direction;
}

// プレイヤーとの衝突
if (place_meeting(x, y, obj_player)) {
    direction = 180 - direction + irandom_range(-10, 10);
    speed *= 1.1;
}

// コンピュータとの衝突
if (place_meeting(x, y, obj_computer)) {
    direction = 180 - direction + irandom_range(-10, 10);
    speed *= 1.1;
}

// スコア更新
if (x <= 0) {
    obj_computer.score += 1;
    game_restart();
} else if (x >= room_width) {
    obj_player.score += 1;
    game_restart();
}

スコア表示のためのコード(Draw GUI Event)

draw_text(50, 50, string(obj_player.score));
draw_text(room_width - 50, 50, string(obj_computer.score));

ルームの設定

新しいルームを作成し、プレイヤー、コンピュータ、ボールのオブジェクトを配置。
この例は基本的なものですが、これを基にさらにゲームを発展させることができます。試してみて、必要に応じて微調整してください!

copilot

ここで、スコアの描画にエラーが発生した。

############################################################################################ ERROR in action number 1 of Draw Event for object obj_ball: Variable <unknown_object>.score(121, -2147483648) not set before reading it. at gml_Object_obj_ball_Draw_64 (line 4) - draw_text(50, 50, string(obj_player.score)); ############################################################################################ gml_Object_obj_ball_Draw_64 (line 4)

エラーメッセージ

このエラーについて、Copilotに直してもらおうとして何回か修正願いを出したが、改善しないので、今までのようにobj_controlを作って解決した。

コントロールオブジェクトのコード(obj_control)

Create Event(作成)

// Creat Event
obj_computer.score = 0;
obj_player.score = 0;

Draw Event(描画)

// Draw Event
draw_text(100, 20, string(obj_player.score));
draw_text(room_width - 100, 20, string(obj_computer.score));

これで起動すると、スコア表示が変わらなかった。
obj_ball内の「// スコア更新」の所が、game_restart()で毎回リセットされるので、ここを変更。

obj_ballのstep eventのスコア更新を以下に変更

// スコア更新
if (x <= 0) {
    obj_computer.score += 1;
    x = xstart; // x初期位置
	y = ystart; // y初期位置
	speed = 3;  // スピードを初期値に戻す
} else if (x >= room_width) {
    obj_player.score += 1;
    x = xstart;
	y = ystart;
	speed = 3; // スピードを初期値に戻す
}

実行すると以下になった

はじめてAIと一緒にGamaMakerで作った卓球

AIと一緒に仕様を追加

ここから少し調整を入れる

調整項目
・ボールのスピードと加速度を調整
・パドルの初期位置をルームに配置した位置に修正

俺の調整

勝利とサーブ権の追加

ここから、前回のブラウザ版でも行った修正をお願いしてみる。

5点取った方が勝利にして、開始時やボールロスト時はボールロストした側からスタートできるようにして
GameMakerで卓球ゲームを5点先取で、ボールをロストした側から再スタートできるように修正します。以下のコードを使って、ゲームの挙動を更新してください。

Copilotの回答

むむむ。Copilotさん!?
前のエラーが出たスコアの所も含めて、全部コードを出してくるよ。

AIのコードを修正する

スコアだけでなく、色んな不具合が出たので自力で修正した。

主な修正
・得点がらみ
 ┗5点取ったら、ウィンドウを開いてWin, Loseを出す
 ┗得点がらみは前から不具合が出ていたので再度obj_controlに移す
・ボールをロストした側から再スタート
 ┗マウスの左クリックで再スタートになってしたのでスペースキーに修正
 ┗得点した側からサーブになるので修正
 ┗敵のサーブの時に目の前のボールを追いかけてしまうので対策
・バウンドの跳ね方
 ┗スプライトの原点が左上基準でコード化されていたので中心に変更
 ┗Copilotは乱数を使っていたのでコリジョンの中心方向に変更

自力解決

AIと一緒に作った卓球の最終形

結果、以下のようになった。

AIと作った卓球・最終版

GameMakerの最新コードは未対応?

Copilotも俺もGameMakerの理解が不十分なので、それぞれのやりたいことが嚙み合わなかったみたい。
こっちは、日本語にローカライズされ、新機能に対応したGameMakerを使っているが、copilotは古いのしか対応してなさそう。

最新のGameMakerには「コリジョン」や「境界線」や「キーを押した時」など色んなイベントがあるのだが、copilotはステップイベントの中で全部やろうとするので、不具合が起きても、俺が理解できない

今回は、自力でコードを書いていたので直すことができた。
コードを全然触ったことなければ、GameMakerで作るのは、おススメできないかな。
UnityやHTML、JAVAとかの方が、AIにコード書いてもらいやすいかもしれない。他の言語も今後は試してみよう。

AIはサンプルプログラムやアドバイスには使えるかも?

1から自分で調べて自力でコードを書くよりは楽。
サンプルコードをもらって、それを元に、自分でコードを書く形で、しばらくやってみようと思う。まさに、共同作業だ。

AIの使い方①:AIに解説してもらう

サンプルプログラムの内容を解説してもらう。
説明は丁寧で、コードを書いていれば理解できる。

ボールをロストした方から再スタートするためのプログラムの正確な個所を教えてください。
ボールをロストした方から再スタートするためのプログラムの正確な箇所は、reset_ball関数と、それを呼び出す箇所にあります。ここでは、どの部分がその処理を行っているかを詳しく説明します。

reset_ball 関数
この関数は、ボールをリセットし、ロストしたプレイヤーが次のラウンドを開始するように設定します。

losing_side は、ボールを失ったプレイヤー(obj_player か obj_computer)を指します。launchingPlayer に losing_side を設定することで、次にボールを発射するプレイヤーが決定されます。

Copilotさんの解説
// Reset ball function
function reset_ball(losing_side) {
    gameStarted = false;
    launchingPlayer = losing_side;
    x = room_width / 2;
    y = room_height / 2;
    speed = 5;
}

Step Event 内のボールリセット処理
ボールがロストした場合に reset_ball 関数を呼び出して、ボールをリセットします。

ボールが左端 (x <= 0) か右端 (x >= room_width) に到達したとき、スコアを更新し、必要に応じて勝者を決定します。
勝者が決定された場合は game_restart 関数を呼び出してゲームをリスタートします。それ以外の場合は reset_ball 関数を呼び出してボールをリセットします。

Copilotさんの解説
// スコア更新
if (x <= 0) { // プレイヤーがボールをロスト
    obj_computer.score += 1;
    if (obj_computer.score >= 5) {
        show_message("Computer Wins!");
        game_restart();
    } else {
        reset_ball(obj_computer); // コンピュータが次にボールを発射
    }
} else if (x >= room_width) { // コンピュータがボールをロスト
    obj_player.score += 1;
    if (obj_player.score >= 5) {
        show_message("Player Wins!");
        game_restart();
    } else {
        reset_ball(obj_player); // プレイヤーが次にボールを発射
    }
}

再スタート時のボールの初期位置設定
Step Event 内で、ゲームが開始されていないときにボールの位置を設定します。
この部分で、ボールをロストしたプレイヤーのパドル位置にボールを設定し、次のラウンドをスタートします。

これでボールをロストした側から再スタートする処理がどのように行われているかがわかります。何か他に質問や修正が必要な点があれば教えてください。

Copilotさんの解説
if (!gameStarted) {
    if (launchingPlayer == obj_player) {
        x = obj_player.x + obj_player.sprite_width;
        y = obj_player.y + obj_player.sprite_height / 2;
    } else if (launchingPlayer == obj_computer) {
        x = obj_computer.x - sprite_width;
        y = obj_computer.y + obj_computer.sprite_height / 2;
    }
    if (mouse_check_button_pressed(mb_left)) {
        gameStarted = true;
        if (launchingPlayer == obj_player) {
            direction = 0; // 右に発射
        } else {
            direction = 180; // 左に発射
        }
    }
}

最終的なコードのサンプル

以下、今回作ったコードのサンプル。

コントロールオブジェクトのコード(obj_control)

Create Event(作成)

// Creat Event
obj_computer.score = 0;
obj_player.score = 0;

Draw Event(描画)

// Draw Event
draw_text(100, 20, string(obj_player.score));
draw_text(room_width - 100, 20, string(obj_computer.score));

プレイヤーオブジェクトのコード(obj_player)

Create Event(作成)

// Create Event

image_blend = c_aqua; // プレイヤーのパドルの色を水色に指定

x = xstart; // xスタート位置をルームに設定した場所に指定
y = ystart; // yスタート位置をルームに設定した場所に指定

spd = 7; // 移動スピード

Step Event(ステップ)

// Step Event
if (keyboard_check(vk_up)) { // 上矢印で上に移動
    y -= spd;
}
if (keyboard_check(vk_down)) { // 下矢印で下に移動
    y += spd;
}
y = clamp(y + speed, sprite_height /2, room_height - sprite_height /2); // めり込み対策を中心点からに変更

コンピュータオブジェクトのコード(obj_computer)

Create Event(作成)

// Create Event
image_blend = c_red; // CPUのパドルカラーを赤に

// Create Event
x = xstart; // ルームに置いた位置を初期位置に設定
y = ystart; // ルームに置いた位置を初期位置に設定

spd = 5; // 移動スピード

Step Event(ステップ)

// Step Event
if (obj_ball.start_game == true) // ボールがリリースされると移動を開始
{
	if (obj_ball.y < y - sprite_height / 4) { // ボールの位置が上の時は上に移動
	    y -= spd;
	} else if (obj_ball.y > y + sprite_height / 4) { // ボールの位置が下の時は下に移動
	    y += spd;
	} else speed = 0; // ボールに追いついたら止まる
	
	y = clamp(y + speed, sprite_height /2, room_height - sprite_height /2); // 画面外対策
}

ボールオブジェクトのコード(obj_ball)

Create Event(作成)

// Create Event

spd = 3; // ボールの初期スピード
a_spd = 0.3; // パドルに当たった時の加速度
speed = spd;

randomize();  // 完全ランダム

// 最初にボールを発射するプレイヤーを抽選
if (irandom(1) = 0) launch_player = obj_player; 
else launch_player = obj_computer;

start_game = false; // ゲーム開始しているか?

// Reset ball function ボールをロストした時の設定
function reset_ball(_losing_side) {
    start_game = false;
    launch_player = _losing_side;
    speed = spd;
}

// Game restart function ゲームのリスタート設定
function game_restart() {
    instance_destroy(); // すべてのインスタンスを削除して再生成
    room_restart(); // ルームを再起動
}

Step Event (ステップ)

// Step Event

// ロストした方からスタート
if (start_game == false) {
    if (launch_player == obj_player) {
        x = obj_player.x + obj_player.sprite_width + 10;
        y = obj_player.y ;
    } else if (launch_player == obj_computer) {
        x = obj_computer.x - obj_computer.sprite_width - 10;
        y = obj_computer.y;
    }

} else {
    x += lengthdir_x(speed, direction);
    y += lengthdir_y(speed, direction);

	// 壁の反射
	if (bbox_bottom > room_height or 0 > bbox_top) 
	{
		vspeed = -vspeed; // 壁に当たったら縦方向の速度を反転
		y = clamp(y, sprite_yoffset, room_height-sprite_yoffset); // 潜らない対策の中心点補正
	}

    // スコア更新
    if (x <= 0) {
        obj_computer.score += 1; // プレイヤーが後逸したらCPUに得点追加
        if (obj_computer.score >= 5) {
            show_message("Computer Wins!"); // 5点取ったらメッセージを表示してリスタート
            game_restart();
        } else {
            reset_ball(obj_player); // プレイヤーにサーブ権
        }
    } else if (x >= room_width) {
        obj_player.score += 1; // CPUが後逸したらプレイヤーに得点追加
        if (obj_player.score >= 5) {
            show_message("Player Wins!"); // 5点取ったらメッセージを表示してリスタート
            game_restart();
        } else {
            reset_ball(obj_computer); // コンピューターにサーブ権
        }
    }
}

press key space event(キープレス・スペース)

if (start_game == false){
	start_game = true;
	if (launch_player == obj_player) {
	    direction = 0; // 右に発射
	} else {
	    direction = 180; // 左に発射
	}
}

Collision Event(obj_computer)

direction = point_direction (other.x, other.y, x, y); // 中心点同士を結ぶ方向に反射
speed += a_spd; // 加速

Collision Event(obj_player)

direction = point_direction (other.x, other.y, x, y); // 中心点同士を結ぶ方向に反射
speed += a_spd; // 加速

今後もAIと相談しながらプログラムしてみる

いいなと思ったら応援しよう!

この記事が参加している募集