Interface編集部
2024年6月号 特集第1部 新開発スタイル…骨格作りから実装まで サポート・ページ
誌面では割愛したリストを掲載します.
第1部 第1章 Pythonでゲームの骨格を作る
リスト4(a)ChatGPTへの指示
* 実装指示
Wallクラスのprepare関数で容器の形状の定義を,draw関数で描画の処理を実装してください. * Wallの形状情報 形状については次の通りとします. (100, 100) – (100, 500) 線分は全て黒色で描画するものとします. |
リスト4(b)ChatGPTからの回答
指示に基づいて,Wall クラスの prepare 関数と draw 関数を実装し,Game クラスでこれらの関数を呼び出すようにコードを修正します.
Wallクラスの更新 |
リスト4(c)生成されたコード
省略
## ウィンドウ中央に表示される壁で囲まれた容器を示すクラス ## ステージの準備を行う # 壁の形状を定義(始点と終点) for start, end in wall_segments: ## ステージの描画を行う ## ゲームの進行を制御するクラス ## ゲームの初期化・準備を行う ## ゲームの画面を描画する ## ゲームの処理を実行する ## ゲームを進行させる self.space.step(1/60) # 物理シミュレーションの進行 pygame.quit() game = Game() |
リスト5(a)ChatGPTへの指示
* 実装指示
容器の内側に丸いブロックを落下させます. 以上の実装および修正コードを出力してください. * 丸いブロックの条件 – 半径は30とする * ブロックの出現位置に関する条件 – 出現位置は,(150, 50) から (500, 50) の座標位置でランダムとする * ブロックの状態に関する条件 – 落下後のブロックは消失することなく容器内にとどまる |
リスト5(-)ChatGPTからの回答
指示に基づいて,新たにBlockクラスを作成し,GameクラスおよびWallクラスに必要な変更を加えます.
Blockクラスの実装 (pythonコード) |
リスト5(b)生成されたコード
import pygame import pymunk import sys import random import time## 丸いブロックを示すクラス class Block: def __init__(self, space): self.space = space self.blocks = [] self.radius = 30 self.color = (255, 0, 0) # 赤色## ブロックの準備を行う def prepare(self): mass = self.radius # 質量を半径と同じにする moment = pymunk.moment_for_circle(mass, 0, self.radius) body = pymunk.Body(mass, moment) body.position = (random.randint(150, 500), 50) # ランダムな位置で出現 shape = pymunk.Circle(body, self.radius) shape.elasticity = 0.6 shape.friction = 0.3 shape.color = self.color self.space.add(body, shape) self.blocks.append(shape)## ブロックの描画を行う def draw(self, screen): for block in self.blocks: p = int(block.body.position.x), int(block.body.position.y) pygame.draw.circle(screen, self.color, p, self.radius)## ウィンドウ中央に表示される壁で囲まれた容器を示すクラス(更新) class Wall: # [以前のコードは変更なし] pass## ゲームの進行を制御するクラス(更新) class Game: def __init__(self): # [以前の初期化コードは変更なし] self.block = Block(self.space) self.last_block_time = 0## ゲームの初期化・準備を行う(更新) def prepare(self): # [以前のコードは変更なし] self.wall.prepare()## ゲームの画面を描画する(更新) def draw(self): # [以前の描画コードは変更なし] self.block.draw(self.screen) pygame.display.update()## ゲームの処理を実行する(更新) def run(self): self.prepare() self.running = True## ゲームを進行させる while self.running: current_time = time.time() if current_time – self.last_block_time > 3: # 3秒ごとにブロックを追加 self.block.prepare() self.last_block_time = current_timefor event in pygame.event.get(): if event.type == pygame.QUIT: self.running = Falseself.space.step(1/60) # 物理シミュレーションの進行 self.draw() # 画面の更新 self.clock.tick(60) # FPSの設定pygame.quit() sys.exit() game = Game() |
リスト6(a)ChatGPTへの指示
* 実装指示
– ブロックの種類を4種類に増やします. |
リスト6(-)ChatGPTからの回答
指示に基づいて,Block クラスを更新して,4種類の異なる色と半径を持つブロックをランダムに生成するようにします.
Blockクラスの更新 |
リスト6(b)生成されたコード
import pygame import pymunk import sys import random import time## 丸いブロックを示すクラス(更新) class Block: def __init__(self, space): self.space = space self.blocks = [] # ブロックの種類:(色, 半径) self.block_types = [ ((255, 0, 0), 15), # 赤, 半径15 ((0, 0, 255), 20), # 青, 半径20 ((0, 255, 0), 35), # 緑, 半径35 ((128, 0, 128), 50) # 紫, 半径50 ]## ブロックの準備を行う(更新) def prepare(self): color, radius = random.choice(self.block_types) # ランダムにブロックの種類を選択 mass = radius # 質量を半径と同じにする moment = pymunk.moment_for_circle(mass, 0, radius) body = pymunk.Body(mass, moment) body.position = (random.randint(150, 500), 50) # ランダムな位置で出現 shape = pymunk.Circle(body, radius) # shape.elasticity = 0.6 # この行は無効化 shape.friction = 0.3 shape.color = color self.space.add(body, shape) self.blocks.append(shape)## ブロックの描画を行う def draw(self, screen): for block in self.blocks: p = int(block.body.position.x), int(block.body.position.y) radius = int(block.radius) color = block.color pygame.draw.circle(screen, color, p, radius)# WallクラスとGameクラスのコードは以前と同様です.game = Game() game.run() |
リスト7(a)ChatGPTへの指示
* 実装指示
– ブロック同士の衝突処理を on_collision 関数として作成してください |
リスト7(-)ChatGPTからの回答
指示に基づいて,ブロック同士の衝突処理をon_collision関数として作成し,衝突したブロックを処理するロジックを実装します.また,必要に応じて新しいブロックを生成する機能も追加します.
実装の概要 Blockクラスの更新 省略されたpythonコード(誌面では割愛) この変更を加えると,Gameクラスのprepareメソッドで衝突ハンドラを設定し,on_collisionメソッドが衝突の開始時に呼び出されるようになります.このメソッドは衝突した2つのボールの色をチェックし,同じ色であれば両方を削除して新しいボールを生成します.生成する新しいボールのタイプは,現在のボールの次のタイプになります.最大サイズのボール同士が衝突した場合,ball_type_indexがボールタイプリストの最後のインデックスになるため,新しいボールは生成されません. このコードは,Pymunkの衝突検出機能を利用しており,衝突イベントが発生するとon_collisionメソッドが自動的に呼び出されます.このメソッド内で適切な衝突後の処理を行うことで,ゲームの物理挙動を定義しています. |
リスト8 ここまで生成させたPythonコードを手動で結合する
省略
class Block: # ブロック同士の衝突処理 def create_block(self, position, color, radius): def next_radius(self, radius): def next_color(self, color): # [Gameクラスのコードは以前と同様ですが,衝突処理の設定を追加] ## ゲームの初期化・準備を行う(更新) game = Game() |
リスト9(a)ChatGPTへの指示
以下のエラーが発生することがあるようです.
Traceback (most recent call last): |
リスト9(b)修正された衝突判定の処理
class Block: def on_collision(self, arbiter, space, data): shape_a, shape_b = arbiter.shapes if shape_a.color == shape_b.color: # 同じ色のブロックの場合 new_radius = self.next_radius(shape_a.radius) if new_radius: # 最大サイズでない場合 new_color = self.next_color(shape_a.color) mid_position = (shape_a.body.position + shape_b.body.position) / 2 self.create_block(mid_position, new_color, new_radius)# 削除する前にリストに存在するか確認 if shape_a in self.blocks: self.blocks.remove(shape_a) space.remove(shape_a.body, shape_a) if shape_b in self.blocks: self.blocks.remove(shape_b) space.remove(shape_b.body, shape_b)return True |
リスト10(a)ChatGPTへの指示
新しいブロックを出現させた際に,壁や床へのめり込みが発生しているようです. これを修正したいと考えます.新しいブロック出現時に壁や床との距離が半径未満になるようであれば,位置を補正してめり込みを回避してください. 壁や床には位置情報のほかに太さのパラメータがあります. Wallクラス内に定義されていますが,これを参照して位置補正の手がかりにしてください. |
リスト10(-)ChatGPTからの回答
新しいブロックが出現した際に壁や床にめり込む問題を解決するには,新しいブロックの出現位置を壁や床から十分な距離に設定する必要があります.Wallクラスに定義されている壁の位置と太さの情報を利用して,新しいブロックの位置を適切に補正します.
以下に,新しいブロックを出現させる際に壁や床との衝突を避けるための位置補正ロジックを実装したコードを示します. (pythonコード) このコードは,新しいブロックが壁や床にめり込まないように,出現位置を壁の太さとブロックの半径を考慮して補正しています.correct_position関数は,新しいブロックのX座標を壁からの距離に基づいて調整します.これにより,新しいブロックが壁や床にめり込む問題を回避できるはずです. |
リスト10(b)修正されたブロック出現部分の処理
class Block: # …def create_block(self, position, color, radius): “”” 新しいブロックを指定された位置,色,半径で出現させる “”” mass = radius moment = pymunk.moment_for_circle(mass, 0, radius) body = pymunk.Body(mass, moment)# 壁との衝突を避けるための位置補正 corrected_position = self.correct_position(position, radius) body.position = corrected_positionshape = pymunk.Circle(body, radius) shape.friction = 0.3 shape.color = color self.space.add(body, shape) self.blocks.append(shape)def correct_position(self, position, radius): “”” 壁や床との衝突を避けるための位置補正を行う “”” wall_thickness = 25 # Wallクラスで定義されている壁の太さ floor_thickness = 25 # 床の太さ(Wallクラスで定義されている場合)min_x = 100 + wall_thickness + radius # 左壁との最小距離 max_x = 550 – wall_thickness – radius # 右壁との最大距離 min_y = 50 # 初期のY位置(壁から離れるために調整) max_y = 500 – floor_thickness – radius # 床との最大距離corrected_x = max(min_x, min(max_x, position[0])) corrected_y = max(min_y, min(max_y, position[1])) corrected_position = (corrected_x, corrected_y) return corrected_position# … |
リスト11 まれに起こる不具合を手動で修正した
def on_collision(self, arbiter, space, data): shape_a, shape_b = arbiter.shapesif shape_a.color == shape_b.color: # 同じ色のブロックの場合 # どちらかが既に削除されていれば処理をスキップ if shape_a not in self.blocks or shape_b not in self.blocks: return False |
第1部 第2章 Unityに移植する
リスト1(a)ChatGPTへ指示
* 目的
これから示すPythonで実装されたゲームプログラムをUnity上で再現します. * 作業条件 私とのやりとりにおいては,日本語を使用してください.英語の回答はしないでください. * 作業指示 Pythonの処理を理解して,Unity C#を用いた記述に変換するための準備をしてください. Pythonコードの理解と準備に関する回答は不要で,移植されたC#コードの出力を優先してください. * 元になるPythonコード (先ほど作ったPythonコード) |
リスト1(-)ChatGPTからの回答
このPythonコードをUnity C#に変換するために,主要な概念と処理を理解する必要があります.Pythonコードは,PygameとPymunkを使用して2D物理ベースのブロック生成ゲームを作成しています.Unityでは,同様の機能をC#で実装することになります.
ゲームの主要な構成要素は以下の通りです: 1. Wallクラス: 固定された壁を定義し,ゲームステージを形成します. |
リスト1(b)生成されたC#コードの抜粋
using UnityEngine; using System.Collections;public class GameController : MonoBehaviour { public GameObject blockPrefab; // ブロックのPrefab public Transform blockContainer; // ブロックの親オブジェクト public float blockRange = 350f; // ブロック出現位置の範囲 private float lastBlockTime; // 最後にブロックを出現させた時間 private float blockInterval = 3f; // ブロック出現間隔(秒)void Start() { StartCoroutine(SpawnBlockRoutine()); }IEnumerator SpawnBlockRoutine() { while (true) { yield return new WaitForSeconds(blockInterval); SpawnBlock(); } }void SpawnBlock() { float xPosition = Random.Range(-blockRange, blockRange); Vector3 spawnPosition = blockContainer.position + new Vector3(xPosition, 0, 0); Instantiate(blockPrefab, spawnPosition, Quaternion.identity, blockContainer); } } |
リスト2(a)ChatGPTへ指示
* 作業条件
GameControllerクラスに以下のメンバを用意します. Color[] blockColors; // ブロックの色 * 作業指示 ブロックの生成時にランダムでブロックの種類を選択するようにしてください. |
リスト2(-)ChatGPTからの回答
指定された要件に従って,GameControllerクラスのブロック生成部分を変更し,Blockクラスに色と半径のパラメータを反映させる方法を示します.
まず,GameControllerクラスに新しいメンバ変数を追加し,ブロック生成時にこれらを使用してランダムにブロックの種類を選択します.そして,Blockクラスにブロックの色と半径を設定する機能を追加します. |
リスト2(b)生成されたC#コード
using UnityEngine; using System.Collections;public class GameController : MonoBehaviour { public GameObject blockPrefab; // ブロックのPrefab public Transform blockContainer; // ブロックの親オブジェクト public float blockRange = 350f; // ブロック出現位置の範囲public Color[] blockColors; // ブロックの色 public float[] blockRadius; // ブロックの半径 public int blockGenRange; // ブロック出現時の種別候補の範囲private float blockInterval = 3f; // ブロック出現間隔(秒)void Start() { StartCoroutine(SpawnBlockRoutine()); }IEnumerator SpawnBlockRoutine() { while (true) { yield return new WaitForSeconds(blockInterval); SpawnBlock(); } }void SpawnBlock() { int blockType = Random.Range(0, blockGenRange); Color selectedColor = blockColors[blockType]; float selectedRadius = blockRadius[blockType];float xPosition = Random.Range(-blockRange, blockRange); Vector3 spawnPosition = blockContainer.position + new Vector3(xPosition, 0, 0);GameObject newBlock = Instantiate(blockPrefab, spawnPosition, Quaternion.identity, blockContainer); newBlock.GetComponent<Block>().SetProperties(selectedColor, selectedRadius); } }using UnityEngine; public class Block : MonoBehaviour // 半径に基づくスケールの設定 |
リスト3(a)ChatGPTへ指示
* 作業条件
通常の画面上部から出現し落下するブロックについては,これまでの実装を維持してランダムに種類を選択してください. 衝突判定関数は Block.cs クラスに void OnCollisionEnter2D(Collision2D collision) メソッドを実装します. 新しいブロック生成処理については,冒頭で示した Pythonコードのcreate_block関数を参考にして,新しい位置の決定を行ってください. * 作業指示 ブロック同士の衝突判定と,衝突時のブロック消去ならびに新ブロックの生成処理を実装してください. |
リスト3(-)ChatGPTからの回答
指定された条件に基づいて,Block.csにOnCollisionEnter2Dメソッドを実装し,衝突時のブロック消去と新ブロックの生成処理を追加します.新しいブロックは消去されるブロックより1ステップ進んだ色とサイズを持ちます.
注意点 |
リスト3(b)生成されたC#コード
// Block.cs using UnityEngine;public class Block : MonoBehaviour { public Color blockColor; // ブロックの現在の色 public float blockRadius; // ブロックの現在の半径 public bool isProcessed = false; // 衝突処理済みかどうかを表すフラグprivate GameController gameController;void Start() { gameController = FindObjectOfType<GameController>(); }public void SetProperties(Color color, float radius) { blockColor = color; blockRadius = radius;Renderer renderer = GetComponent<Renderer>(); if (renderer != null) { renderer.material.color = color; }transform.localScale = new Vector3(radius, radius, radius); }void OnCollisionEnter2D(Collision2D collision) { Block otherBlock = collision.gameObject.GetComponent<Block>(); if (otherBlock != null && !otherBlock.isProcessed && !this.isProcessed && otherBlock.blockColor == this.blockColor) { this.isProcessed = true; otherBlock.isProcessed = true;Vector3 newPosition = (this.transform.position + otherBlock.transform.position) / 2; gameController.CreateNextBlock(newPosition, this.blockColor, this.blockRadius); Destroy(otherBlock.gameObject); Destroy(this.gameObject); } } }// GameController.cs using UnityEngine; public class GameController : MonoBehaviour // 新しいブロックを出現させるメソッド // 次の色とサイズを選択 // 新しいブロックの位置を補正する GameObject newBlock = Instantiate(blockPrefab, position, Quaternion.identity, blockContainer); private Vector3 CorrectPosition(Vector3 position, float radius) |