昨天時間緊迫,測試時有發現道具生成在障礙物上,所以今天打算請AI教我如何避開障礙物來進行生成這步驟。
這次的想法比較簡單:
原本只負責隨機生成上方或下方岩石,這次增加一個變數,記錄最後一次生成的是上方岩石還是下方岩石。
這樣 ItemSpawner 就可以取得這個資訊,再決定道具要生成在哪一側。
using UnityEngine;
public class RockSpawner : MonoBehaviour
{
[Header("岩石 Prefab 設定")]
public GameObject topRockPrefab;
public GameObject bottomRockPrefab;
[Header("生成位置設定")]
public float topYPosition = 3.5f;
public float bottomYPosition = -3.5f;
public float yOffset = 0.5f;
[Header("生成頻率與速度")]
public float spawnRate = 2f;
public float currentSpeed = 4f;
public float speedIncreaseAmount = 0.8f;
public float speedUpInterval = 30f;
// 【新增】:紀錄最後一次生成的是否為上方岩石
[HideInInspector]
public bool lastSpawnedIsTop = false;
private float spawnTimer = 0f;
private float speedTimer = 0f;
void Start()
{
SpawnRock();
}
void Update()
{
spawnTimer += Time.deltaTime;
if (spawnTimer >= spawnRate)
{
SpawnRock();
spawnTimer = 0f;
}
speedTimer += Time.deltaTime;
if (speedTimer >= speedUpInterval)
{
currentSpeed += speedIncreaseAmount;
if (spawnRate > 0.8f)
{
spawnRate -= 0.1f;
}
BackgroundRepeat[] scrollables = FindObjectsOfType<BackgroundRepeat>();
foreach (var bg in scrollables)
{
bg.UpdateSpeed(currentSpeed);
}
speedTimer = 0f;
}
}
void SpawnRock()
{
// 50% 機率生成上岩石,50% 機率生成下岩石
lastSpawnedIsTop = Random.value > 0.5f;
GameObject prefabToSpawn = lastSpawnedIsTop ? topRockPrefab : bottomRockPrefab;
float targetY = lastSpawnedIsTop ? topYPosition : bottomYPosition;
targetY += Random.Range(-yOffset, yOffset);
Vector3 spawnPosition = new Vector3(transform.position.x, targetY, 0);
GameObject newRock = Instantiate(prefabToSpawn, spawnPosition, Quaternion.identity);
PipeMove rockMove = newRock.GetComponent<PipeMove>();
if (rockMove != null)
{
rockMove.moveSpeed = currentSpeed;
}
}
}
這次主要增加的是 lastSpawnedIsTop。
它會記錄最近一次生成的岩石位置,讓其他程式可以利用這個資訊判斷道具應該往哪裡生成。
接著修改昨天的 ItemSpawner。
這次不再單純讓道具在固定範圍內隨機生成,而是先確認最近生成的岩石是在上方還是下方,再讓道具優先出現在另一側。
例如上方有岩石時,道具就放在中下方;下方有岩石時,道具則放在中上方。
using UnityEngine;
public class ItemSpawner : MonoBehaviour
{
[Header("道具清單")]
[Tooltip("羽毛或紫色方塊等道具 Prefab")]
public GameObject[] itemPrefabs;
[Header("生成時間範圍(秒)")]
public float minSpawnInterval = 5f;
public float maxSpawnInterval = 15f;
[Header("生成高度設定(錯位分流)")]
[Tooltip("當右側刷出【上方岩石】時,道具優先出現的 Y 軸位置(中下方)")]
public float itemYWhenTopRockSpawns = -1.5f;
[Tooltip("當右側刷出【下方岩石】時,道具優先出現的 Y 軸位置(中上方)")]
public float itemYWhenBottomRockSpawns = 1.5f;
[Tooltip("Y 軸隨機微調幅度(增添高低變化)")]
public float yOffset = 0.3f;
[Header("關聯的 RockSpawner")]
public RockSpawner rockSpawner;
private float timer = 0f;
private float nextSpawnTime = 0f;
void Start()
{
if (rockSpawner == null)
{
rockSpawner = FindObjectOfType<RockSpawner>();
}
ResetTimer();
}
void Update()
{
timer += Time.deltaTime;
if (timer >= nextSpawnTime)
{
SpawnRandomItem();
ResetTimer();
}
}
void ResetTimer()
{
timer = 0f;
nextSpawnTime = Random.Range(minSpawnInterval, maxSpawnInterval);
}
void SpawnRandomItem()
{
if (itemPrefabs == null || itemPrefabs.Length == 0) return;
int randomIndex = Random.Range(0, itemPrefabs.Length);
GameObject selectedItem = itemPrefabs[randomIndex];
float targetY = 0f;
// 【空間分流邏輯】:根據岩石生成位置,讓道具優先生成在另一側的空間
if (rockSpawner != null)
{
targetY = rockSpawner.lastSpawnedIsTop ? itemYWhenTopRockSpawns : itemYWhenBottomRockSpawns;
}
else
{
targetY = Random.Range(-1.5f, 1.5f);
}
// 微幅隨機偏移,增加視覺變化
targetY += Random.Range(-yOffset, yOffset);
Vector3 spawnPosition = new Vector3(transform.position.x, targetY, 0f);
GameObject newItem = Instantiate(selectedItem, spawnPosition, Quaternion.identity);
// 複用 PipeMove 組件同步移動速度
PipeMove itemMove = newItem.GetComponent<PipeMove>();
if (itemMove != null && rockSpawner != null)
{
itemMove.moveSpeed = rockSpawner.currentSpeed;
}
}
}
這次修改主要是讓道具的生成位置不再完全隨機,而是會參考目前障礙物的生成方向。
這樣在遊玩時,道具比較不容易直接出現在岩石裡面,也能讓玩家有比較合理的路線可以取得道具。
今天主要是修改道具的生成方式,讓道具可以參考障礙物的位置來決定生成高度。
原本道具只是隨機出現在場景中,實際測試後發現有可能和岩石重疊,因此這次增加了岩石位置的判斷,讓道具優先生成在障礙物的另一側。
這次也是在之前的程式基礎上繼續修改,而不是重新製作新的系統。
目前遊戲的基本架構已經慢慢建立起來,接下來會繼續嘗試加入道具效果,看看這個小遊戲最後能做到什麼程度。