seedButton 클릭 시 → 씨앗 심기 모드(FieldState.Seed)로 전환하고 오른쪽 씨앗 선택 UI 활성화
harvestButton 클릭 시 → 수확 모드(FieldState.Harvest)로 전환하고 씨앗 선택 UI 비활성화
plantButtons[] 각각 클릭 시 → 해당 인덱스 식물 심기 실행 (SetPlant(j) 호출)
클로저 이슈 방지를 위해 int j = i; 로 로컬 변수에 담아 사용
2. OnSeedButton() – 씨앗 심기 모드 전환
FieldState.Seed로 상태 변경
오른쪽 씨앗 선택 UI(seedUI) 활성화
3. OnHarvestButton() – 수확 모드 전환
FieldState.Harvest로 상태 변경
씨앗 선택 UI 비활성화
4. ActivateFieldUI(bool isActive) – 왼쪽 필드 UI 제어
왼쪽 UI(fieldUI)를 켜거나 끄는 메서드
필드에 들어갈 때 켜고, 나올 때 끄는 식으로 외부에서 호출 가능
🚨 클로저 이슈
람다식은 변수의 값이 아니라 변수 그 자체를 참조한다.
for (int i = 0; i < N; i++)에서 i는 루프 전체를 통틀어 하나뿐인 동일 변수다. 버튼을 누르는 시점은 보통 루프가 끝난 나중 시점이라 그때 i의 값은 이미 N(마지막 + 1)이 되어 있다. 그래서 다음 코드는 모든 버튼 리스너가 동일한 i(=N)를 사용하게 된다 → 인덱스 범위 초과/모두 같은 동작
따라서 i의 값을 복사한 지역 변수를 만들어 활용해 주었다.
// 필드 매니저 스크립트
public enum FieldState { None, Seed, Harvest }
[SerializeField] private int currentPlantIndex;
[SerializeField] private GameObject[] plants;
void Update()
{
if (fieldState != FieldState.None)
{
switch (fieldState)
{
case FieldState.Seed:
OnSeed();
break;
case FieldState.Harvest:
OnHarvest();
break;
}
}
}
private void OnSeed()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100f, fieldLayerMask))
{
Tile tile = hit.collider.GetComponent<Tile>();
int tileX = tile.arrayPos.x;
int tileY = tile.arrayPos.y;
if (tileArray[tileX, tileY] == null)
{
GameObject plant = Instantiate(plants[currentPlantIndex], transform.GetChild(1));
plant.transform.position = hit.transform.position;
plant.GetComponent<Plant>().plantIndex = currentPlantIndex;
tileArray[tileX, tileY] = plant;
}
}
}
}
public void SetPlant(int index)
{
currentPlantIndex = index;
}
public void SetState(FieldState newState)
{
if (fieldState != newState)
{
fieldState = newState;
}
}
1. Plant 스크립트와 연동
심을 때 plant.plantIndex 값을 설정해 이후 수확 로직에서 참조 가능
3. 농작물 수확
농작물 심기와 성장을 구현하였으니
이번에는 농작물 수확을 구현하려고 한다.
using UnityEngine;
public class BillboardCamera : MonoBehaviour
{
private Transform mainCamera;
void Start()
{
mainCamera = Camera.main.transform;
}
void LateUpdate()
{
transform.LookAt(mainCamera.transform);
}
}
우선 농작물이 모두 성장하면 머리 위에 느낌표 프리팹을 띄워 플레이어가 한눈에 알아볼 수 있도록 했다. 이 오브젝트에는 BillboardCamera 스크립트를 적용해 언제나 카메라를 향하도록 만들어 시야 각도와 상관없이 표시가 잘 보이게 했다.
using System.Collections;
using UnityEngine;
public class FieldManager : MonoBehaviour
{
private void OnHarvest()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100f, fieldLayerMask))
{
Tile tile = hit.collider.GetComponent<Tile>();
int tileX = tile.arrayPos.x;
int tileY = tile.arrayPos.y;
if (tileArray[tileX, tileY] != null)
{
Plant plant = tileArray[tileX, tileY].GetComponent<Plant>();
if (plant.isHarvest)
{
tileArray[tileX, tileY].SetActive(false);
tileArray[tileX, tileY] = null;
StartCoroutine(HarvestRoutine(plant.plantIndex, hit.transform.position));
}
}
}
}
}
IEnumerator HarvestRoutine(int index, Vector3 pos)
{
int renAmount = Random.Range(1, 4);
for (int i = 0; i < renAmount; i++)
{
GameObject crop = Instantiate(crops[index], transform.GetChild(2));
crop.transform.position = pos + Vector3.up * 0.5f;
Rigidbody cropRb = crop.GetComponent<Rigidbody>();
float ranX = Random.Range(-2f, 2f);
float ranZ = Random.Range(-2f, 2f);
var forceDir = new Vector3(ranX, 5f, ranZ);
cropRb.AddForce(forceDir, ForceMode.Impulse);
yield return new WaitForSeconds(0.15f);
}
yield return null;
}
}
using System;
using System.Collections;
using UnityEngine;
using Random = UnityEngine.Random;
public enum WeatherType { Sun, Rain, Snow }
public class WeatherSystem : MonoBehaviour
{
public WeatherType weatherType;
public event Action<WeatherType> weatherAction;
[SerializeField] private GameObject[] weatherParticles;
IEnumerator Start()
{
while (true)
{
yield return new WaitForSeconds(15f);
int weatherCount = Enum.GetValues(typeof(WeatherType)).Length;
Debug.Log(weatherCount);
int ranIndex = Random.Range(0, weatherCount);
weatherType = (WeatherType)ranIndex;
foreach (var particle in weatherParticles)
particle.SetActive(false);
weatherParticles[ranIndex].SetActive(true);
weatherAction?.Invoke(weatherType);
}
}
}
public class Plant : MonoBehaviour
{
private void SetGrowth(WeatherType weatherType)
{
switch (weatherType)
{
case WeatherType.Sun:
// 성장 최대
break;
case WeatherType.Rain:
// 성장 중간
break;
case WeatherType.Snow:
// 성장 최소
break;
}
}
}
15초마다 Sun / Rain/ Snow 중 하나를 랜덤으로 선택하고 해당 파티클만 활성화