2024年6月号 特集第1部 新開発スタイル…骨格作りから実装まで サポート・ページ

誌面では割愛したリストを掲載します.

 

第1部 第1章 Pythonでゲームの骨格を作る

リスト4(a)ChatGPTへの指示

* 実装指示

Wallクラスのprepare関数で容器の形状の定義を,draw関数で描画の処理を実装してください.
またGameクラスにWallクラスで実装した描画のコードを呼び出すようにしてください.
それぞれ修正したコードを出力してください.

* Wallの形状情報

形状については次の通りとします.
(始点)-(終点) という形式で長方形の中心を通る線分を定義します.
いずれの線分も太さを25として,長方形を構成します.

(100, 100) – (100, 500)
(550, 100) – (550, 500)
(100, 500) – (550, 500)

線分は全て黒色で描画するものとします.

 

リスト4(b)ChatGPTからの回答

指示に基づいて,Wall クラスの prepare 関数と draw 関数を実装し,Game クラスでこれらの関数を呼び出すようにコードを修正します.

Wallクラスの更新
prepare 関数では,壁の形状を定義します.この場合,pymunkを使用して壁を物理オブジェクトとして扱います.
draw 関数では,pygameを使用して壁を画面に描画します.
Gameクラスの更新
draw 関数内で,Wallクラスの draw 関数を呼び出します.
以下に,更新されたコードを示します.

 

リスト4(c)生成されたコード

省略

## ウィンドウ中央に表示される壁で囲まれた容器を示すクラス
class Wall:
def __init__(self, space):
self.space = space
self.walls = []

## ステージの準備を行う
def prepare(self):
wall_thickness = 25
wall_color = (0, 0, 0) # 黒色

# 壁の形状を定義(始点と終点)
wall_segments = [
((100, 100), (100, 500)),
((550, 100), (550, 500)),
((100, 500), (550, 500))
]

for start, end in wall_segments:
body = pymunk.Body(body_type=pymunk.Body.STATIC)
shape = pymunk.Segment(body, start, end, wall_thickness)
shape.elasticity = 1.0
shape.friction = 1.0
shape.color = wall_color
self.space.add(body, shape)
self.walls.append(shape)

## ステージの描画を行う
def draw(self, screen):
for wall in self.walls:
body = wall.body
pv1 = body.position + wall.a.rotated(body.angle)
pv2 = body.position + wall.b.rotated(body.angle)
pygame.draw.lines(screen, wall.color, False, [pv1, pv2], int(wall.radius * 2))

## ゲームの進行を制御するクラス
class Game:
def __init__(self):
pygame.init()
self.width, self.height = 800, 600
self.screen = pygame.display.set_mode((self.width, self.height))
self.clock = pygame.time.Clock()
self.space = pymunk.Space()
self.space.gravity = (0, 900)
self.wall = Wall(self.space)

## ゲームの初期化・準備を行う
def prepare(self):
self.wall.prepare()

## ゲームの画面を描画する
def draw(self):
self.screen.fill((255, 255, 255)) # 白色で画面をクリア
self.wall.draw(self.screen) # 壁の描画
pygame.display.update()

## ゲームの処理を実行する
def run(self):
self.prepare()
self.running = True

## ゲームを進行させる
while self.running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False

self.space.step(1/60) # 物理シミュレーションの進行
self.draw() # 画面の更新
self.clock.tick(60) # FPSの設定

pygame.quit()
sys.exit()

game = Game()
game.run()

 

リスト5(a)ChatGPTへの指示

* 実装指示

容器の内側に丸いブロックを落下させます.
ブロックの実装は Wallクラスを参考にして,Blockクラスを作成してください.
ブロックの初期化コードはBlockクラスのprepare関数に,描画コードはdraw関数に実装してください.
ブロックの処理のために,Gameクラスに変更が必要です.
壁との衝突処理において,Wallクラスのprepare関数に変更が必要と考えられます.

以上の実装および修正コードを出力してください.

* 丸いブロックの条件

– 半径は30とする
– 色を赤とする
– 半径と同サイズの質量パラメータを設定

* ブロックの出現位置に関する条件

– 出現位置は,(150, 50) から (500, 50) の座標位置でランダムとする
– ブロックは3秒間隔で出現させる

* ブロックの状態に関する条件

– 落下後のブロックは消失することなく容器内にとどまる
– 壁や床,他のブロックと衝突し,衝突の際には物理挙動に従う

 

リスト5(-)ChatGPTからの回答

指示に基づいて,新たにBlockクラスを作成し,GameクラスおよびWallクラスに必要な変更を加えます.

Blockクラスの実装
prepare関数でブロックの物理属性を設定します.
draw関数でブロックを画面に描画します.
Gameクラスの更新
ブロックの生成と管理を追加します.
3秒ごとに新しいブロックを生成します.
Wallクラスの更新
壁とブロックの衝突処理を適切に扱うための設定を追加します.
以下に,更新されたコードを示します.

(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()
game.run()

 

リスト6(a)ChatGPTへの指示

* 実装指示

– ブロックの種類を4種類に増やします.
– (色,半径): (赤, 15),(青, 20),(緑, 35),(紫, 50)
– 各ブロックの質量パラメータは半径と合わせてください
– ランダムにブロックを出現させる際には,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 関数として作成してください
– 同じ種類の衝突した2つのブロックは消去し,2点間の中心位置に新しいブロックを生成してください
– 新しいブロックは,次のブロックタイプで生成してください
– 最大のブロックサイズ同士が衝突した場合には,新しいブロックの生成は不要です
– 新しいブロックの生成時に,壁や床と衝突するようであれば衝突しない位置に補正してください

 

リスト7(-)ChatGPTからの回答

指示に基づいて,ブロック同士の衝突処理をon_collision関数として作成し,衝突したブロックを処理するロジックを実装します.また,必要に応じて新しいブロックを生成する機能も追加します.

実装の概要
pymunkの衝突コールバック機能を使用して,ブロック同士の衝突を検出します.
衝突したブロックが同じ種類であれば,それらを消去し,新しいブロックを生成します.
最大サイズのブロック同士の衝突では新しいブロックは生成しません.
新しいブロックの生成位置が壁や床と衝突しないように調整します.

Blockクラスの更新
on_collision関数を追加して,衝突処理を実装します.
新しいブロックを生成する際には,衝突する位置を避けるようにします.
以下に,更新されたコードの一部を示します.全体のコードは非常に長くなるため,重要な部分に絞って説明します.

省略されたpythonコード(誌面では割愛)

この変更を加えると,Gameクラスのprepareメソッドで衝突ハンドラを設定し,on_collisionメソッドが衝突の開始時に呼び出されるようになります.このメソッドは衝突した2つのボールの色をチェックし,同じ色であれば両方を削除して新しいボールを生成します.生成する新しいボールのタイプは,現在のボールの次のタイプになります.最大サイズのボール同士が衝突した場合,ball_type_indexがボールタイプリストの最後のインデックスになるため,新しいボールは生成されません.

このコードは,Pymunkの衝突検出機能を利用しており,衝突イベントが発生するとon_collisionメソッドが自動的に呼び出されます.このメソッド内で適切な衝突後の処理を行うことで,ゲームの物理挙動を定義しています.

 

リスト8 ここまで生成させたPythonコードを手動で結合する

省略

class Block:
# [以前の初期化とprepare関数は変更なし]

# ブロック同士の衝突処理
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)
space.remove(shape_a.body, shape_a)
space.remove(shape_b.body, shape_b)
self.blocks.remove(shape_a)
self.blocks.remove(shape_b)
return True

def create_block(self, position, color, radius):
“”” 新しいブロックを指定された位置,色,半径で出現させる “””
mass = radius
moment = pymunk.moment_for_circle(mass, 0, radius)
body = pymunk.Body(mass, moment)
body.position = position # 壁や床との衝突を避けるための位置補正が必要
shape = pymunk.Circle(body, radius)
shape.friction = 0.3
shape.color = color
self.space.add(body, shape)
self.blocks.append(shape)

def next_radius(self, radius):
“”” 次のブロックの半径を決定する “””
# 半径のリストを作成し,次の半径を返す
radius_list = [15, 20, 35, 50]
if radius in radius_list:
index = radius_list.index(radius)
if index < len(radius_list) – 1:
return radius_list[index + 1]
return None # 最大サイズの場合はNoneを返す

def next_color(self, color):
“”” 次のブロックの色を決定する “””
# 色のリストを作成し,次の色を返す
color_list = [(255, 0, 0), (0, 0, 255), (0, 255, 0), (128, 0, 128)]
if color in color_list:
index = color_list.index(color)
if index < len(color_list) – 1:
return color_list[index + 1]
return color_list[0] # 最大サイズの場合は最初の色に戻る

# [Gameクラスのコードは以前と同様ですが,衝突処理の設定を追加]
class Game:
# [以前の初期化とその他の関数は変更なし]

## ゲームの初期化・準備を行う(更新)
def prepare(self):
self.wall.prepare()
handler = self.space.add_collision_handler(0, 0) # 0はデフォルトの衝突タイプ
handler.begin = self.block.on_collision

game = Game()
game.run()

 

リスト9(a)ChatGPTへの指示

以下のエラーが発生することがあるようです.

Traceback (most recent call last):
File “D:\ball-falling-game\game.py”, line 88, in on_collision
self.blocks.remove(shape_a)
ValueError: list.remove(x): x not in list

 

リスト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上で再現します.
UnityではC#を使用した開発のため,Pythonコードからの変換を行います.

* 作業条件

私とのやりとりにおいては,日本語を使用してください.英語の回答はしないでください.
ブロックの挙動に関するプログラムを Block.cs に記述します.
ゲームの進行に関するプログラムを GameController.cs に記述します.
まずは,GameControllerクラスにブロックを生成するコードを実装してください.
ブロックのPrefabは blockPrefab メンバーで与えられるものとします.
ブロックはblockContainerオブジェクトの子供として生成してください.

* 作業指示

Pythonの処理を理解して,Unity C#を用いた記述に変換するための準備をしてください.
画面の上部でブロックを3秒おきに出現させ,容器内に落下する実装を行ってください.
ブロックの生成位置の制御のため,blockRangeのメンバ変数を用意してください.
生成位置はx座標を-blockRange, +blockRangeの範囲内として,y座標は0とします.
これは,blockContainerオブジェクトの位置からの相対の位置指定です.

Pythonコードの理解と準備に関する回答は不要で,移植されたC#コードの出力を優先してください.

* 元になるPythonコード

(先ほど作ったPythonコード)

 

リスト1(-)ChatGPTからの回答

このPythonコードをUnity C#に変換するために,主要な概念と処理を理解する必要があります.Pythonコードは,PygameとPymunkを使用して2D物理ベースのブロック生成ゲームを作成しています.Unityでは,同様の機能をC#で実装することになります.

ゲームの主要な構成要素は以下の通りです:

1. Wallクラス: 固定された壁を定義し,ゲームステージを形成します.
2. Blockクラス: さまざまな色とサイズのブロックを生成し,物理的な動作をシミュレートします.
3. Gameクラス: ゲームの進行を管理し,ブロックの生成とゲームループを制御します.
Unityでは,物理シミュレーションにRigidbodyとColliderコンポーネントを使用し,ゲームオブジェクトの生成と管理にはPrefabとGameObjectクラスを使用します.また,Pygameの描画関数は,UnityのSpriteRendererやCanvasを用いたレンダリングに置き換える必要があります.

 

リスト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; // ブロックの色
float[] blockRadius; // ブロックの半径
int blockGenRange; // ブロック生成時の種別候補の範囲

* 作業指示

ブロックの生成時にランダムでブロックの種類を選択するようにしてください.
ブロック種別の範囲は,0からblockGenRange の範囲とします.
このインデックスを用いて,ブロックの色および半径を選択してください.
GameControllerクラスのブロック生成箇所およびBlockクラスへのパラメータ反映などソースコードの修正を行い,結果を出力してください.

 

リスト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
{
public void SetProperties(Color color, float radius)
{
// 色の設定
Renderer renderer = GetComponent<Renderer>();
if (renderer != null)
{
renderer.material.color = color;
}

// 半径に基づくスケールの設定
// ここでは半径が1単位のスケールに相当すると仮定します
// 実際のスケールはゲームのデザインによって調整が必要です
transform.localScale = new Vector3(radius, radius, radius);
}
}

 

リスト3(a)ChatGPTへ指示

* 作業条件

通常の画面上部から出現し落下するブロックについては,これまでの実装を維持してランダムに種類を選択してください.
衝突により消去されるの2つのブロックを再び衝突判定で使用してはいけません.

衝突判定関数は Block.cs クラスに void OnCollisionEnter2D(Collision2D collision) メソッドを実装します.
衝突判定後の処理については,冒頭で示した Pythonコードのon_collision関数の内容を参考にしてください.

新しいブロック生成処理については,冒頭で示した Pythonコードのcreate_block関数を参考にして,新しい位置の決定を行ってください.

* 作業指示

ブロック同士の衝突判定と,衝突時のブロック消去ならびに新ブロックの生成処理を実装してください.
新ブロックは,消去されるブロックより1ステップ進んだ色,サイズを選択してください.
ただし,BlockColorsとして登録されている個数を超える場合には,最終ブロックのため新しいブロック生成をキャンセルとします.

 

リスト3(-)ChatGPTからの回答

指定された条件に基づいて,Block.csにOnCollisionEnter2Dメソッドを実装し,衝突時のブロック消去と新ブロックの生成処理を追加します.新しいブロックは消去されるブロックより1ステップ進んだ色とサイズを持ちます.

注意点
CorrectPositionメソッドは,新しいブロックが壁や他のオブジェクトと衝突しないように位置を補正します.この関数はゲームの具体的なデザインや設定に応じて調整する必要があります.
isProcessedフラグは,衝突処理が複数回行われるのを防ぐために使用されます.これにより,衝突判定後にすぐに消去されたブロックが再度衝突判定に使われることを防ぎます.
新しいブロックの生成位置は,衝突した2つのブロックの中間点を基にしています.この位置はゲームのデザインに応じて調整する必要があります.

 

リスト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
{
// … その他のメンバ変数とメソッド

// 新しいブロックを出現させるメソッド
public void CreateNextBlock(Vector3 position, Color color, float radius)
{
int colorIndex = System.Array.IndexOf(blockColors, color);
if (colorIndex < 0 || colorIndex >= blockColors.Length – 1)
{
// 最後の色に到達したか,色が見つからない場合は何もしない
return;
}

// 次の色とサイズを選択
Color nextColor = blockColors[colorIndex + 1];
float nextRadius = blockRadius[colorIndex + 1];

// 新しいブロックの位置を補正する
position = CorrectPosition(position, nextRadius);

GameObject newBlock = Instantiate(blockPrefab, position, Quaternion.identity, blockContainer);
newBlock.GetComponent<Block>().SetProperties(nextColor, nextRadius);
}

private Vector3 CorrectPosition(Vector3 position, float radius)
{
// 壁や床との衝突を避けるための位置補正を行う
// この部分はゲームの具体的なデザインに応じて調整が必要です
// 例: position.x = Mathf.Clamp(position.x, minX, maxX);
// 例: position.y = Mathf.Clamp(position.y, minY, maxY);
return position;
}
}