Unity 5로 '유니티와 C#으로 배우는 게임 개발 교과서'
26장 객체 지향적 사고의 군집행동 예제 따라하기. 완성되었는데 현재는 마우스 있는 쪽으로 추적하는 기능이 먹히지
않고 있는 중.
책에서는 카메라에 부착시킨 BoidSpawner.cs를 빈 게임 오브젝트 생성 뒤 'BoidSpawner'로 명명한
게임오브젝트에 부착하였기에 카메라에서 화면으로 향하는
//마우스 위치 추적
Vector3 mousePos2d = new Vector3 (Input.mousePosition.x, Input.mousePosition.y,
mCamera.transform.position.y);
mousePos = mCamera.ScreenToWorldPoint (mousePos2d);
이 부분 코드가 작동하지 않아 생긴 문제였다.
private Camera mCamera; 로 카메라용 변수를 만들고 Start()함수에서
mCamera = GameObject.Find ("_Main Camera").GetComponent<Camera>
(); 로 설정하여 문제를 바로 잡다.
16/11/5 월
|
BoidSpawner.cs=========================================
//Hierarchy탭-BoidsSpawner에 적용.
//보이드들 정해진 수 대로 생성하기, 각종 수치 지정
//마우스 위치 추적하기
using UnityEngine;
using System.Collections;
public class BoidSpawner : MonoBehaviour {
public static BoidSpawner S; //싱글톤-어디서건 참조 가능.
public int numBoids = 100;
public GameObject boidPrefab;
public float spawnRadius = 100f; //초기 생성시 범위
public float spawnVelocity = 10f; //생성시 속도
public float minVelocity = 0f; //최저 속도
public float maxVelocity = 30f; //최고 속도
public float nearDist = 30f; //이웃 거리
public float collisionDist = 5f; //충돌 위험 거리
public float velocityMatchingAmt = 0.01f; //인접 이웃간 속도 조정용
public float flockCenteringAmt = 0.15f; //무리 중앙 조정용
public float collisionAvoidanceAmt = -0.5f; //충돌 회피 정도 조정용
public float mouseAttractionAmt = 0.01f; //마우스 추적 정도 조정용
public float mouseAvoidanceAmt = 0.75f; //마우스 회피 정도 조정용
public float mouseAvoidanceDist = 15f; //마우스 회피 거리
public float velocityLerpAmt = 0.25f; //속도 보간용
public bool __________________; //인스펙터 창 구분용
public Vector3 mousePos; //마우스 위치 저장용
private Camera mCamera; //메인 카메라의 카메라 컴포넌트 저장용
void Start () {
//싱글톤 S를 BoidSpawner의 현재 인스턴스로 설정
S = this;
//numBoids 만큼 Boid 인스턴스화
for (int i = 0; i < numBoids; i++) {
Instantiate (boidPrefab);
}
//메인 카메라의 카메라 컴포넌트 참조를 간단히 하기 위한 적용
mCamera = GameObject.Find ("_Main Camera").GetComponent ();
}
void LateUpdate () {
//마우스 위치 추적
Vector3 mousePos2d = new Vector3 (Input.mousePosition.x, Input.mousePosition.y,
mCamera.transform.position.y);
mousePos = mCamera.ScreenToWorldPoint (mousePos2d);
}
}
Boid.cs=============================================
//Project탭-BoidPref에 적용.
//보이드들의 행동 구현.
//기본적으로 마우스의 위치를 향해 다가가고 마우스와 너무 가까우면 도망간다.
//이동 동안 주변 보이드와 거리와 속도를 유지하며 이동한다.
using UnityEngine;
using System.Collections;
using System.Collections.Generic; //List 사용용
public class Boid : MonoBehaviour {
//이 정적 리스트는 모든 Boid 인스턴스를 저장, 공유됨
public static List boids;
//Rigidbody를 사용치 않고 속도를 직접 처리
public Vector3 velocity; //현재 속도
public Vector3 newVelocity; //다음 프레임의 속도
public Vector3 newPosition; //다음 프레임의 위치
public List neighbors; //인접한 모든 Boid
public List collisionRisks; //너무 가까워 충돌위험인 모든 Boid
public Boid closest; //가장 가까운 단일 Boid
void Awake () { //Boid 초기화
//boids 리스트가 null일 경우 이를 정의.
if (boids == null) {
boids = new List();
}
boids.Add (this); //이 Boid를 boids에 추가
//이 Boid 임의의 위치와 속도 지정.
Vector3 randPos = Random.insideUnitSphere * BoidSpawner.S.spawnRadius;
randPos.y = 0; //Boid가 xz 평면에서만 움직이도록 제한
this.transform.position = randPos;
velocity = Random.onUnitSphere;
velocity *= BoidSpawner.S.spawnVelocity;
//두 리스트 초기화
neighbors = new List ();
collisionRisks = new List();
//this.transform을 Boids 게임오브젝트의 하위 게임오브젝트로 설정
//수많은 보이드 인스턴스들을 하나의 게임오브젝트 하위에 생성시켜
//Hierarchy를 깨끗하게 유지하는 팁.*****************
this.transform.parent = GameObject.Find ("Boids").transform;
//Boid에 임의의 색 지정, 너무 어둡지 않게 처리
Color randColor = Color.black;
while (randColor.r + randColor.g + randColor.b < 1.0f) {
randColor = new Color(Random.value, Random.value, Random.value);
}
Renderer[] rends = gameObject.GetComponentsInChildren ();
foreach (Renderer r in rends) {
r.material.color = randColor;
}
}
void Update(){
//인접한 Boid의 리스트 얻기(이 Boid의 이웃)
List neighbors = GetNeighbors (this);
//newVelocity와 newPosition을 현재 값으로 초기화
newVelocity = velocity;
newPosition = this.transform.position;
//**속도일치: 인접 이웃과 비슷하게 조정
Vector3 neighborVel = GetAverageVelocity (neighbors);
//BoidSpawner.S 싱글톤에 설정된 필드 활용
newVelocity += neighborVel * BoidSpawner.S.velocityMatchingAmt;
//**군집 위치 조정: 이웃의 가운데 방향으로 이동
Vector3 neighborCenterOffset = GetAveragePosition(neighbors) - this.transform.position;
newVelocity += neighborCenterOffset * BoidSpawner.S.flockCenteringAmt;
//**충돌 회피: 너무 가까운 일원과 충돌하지 않게 처리
Vector3 dist;
if (collisionRisks.Count > 0) {
Vector3 collisionAveragePos = GetAveragePosition(collisionRisks);
dist = collisionAveragePos - this.transform.position;
newVelocity += dist * BoidSpawner.S.collisionAvoidanceAmt;
}
//**마우스 추적: 마우스가 있는 방향으로 이동
dist = BoidSpawner.S.mousePos - this.transform.position;
if (dist.magnitude > BoidSpawner.S.mouseAvoidanceDist) {
newVelocity += dist * BoidSpawner.S.mouseAttractionAmt;
} else {
//마우스가 너무 가까이 있으면 빠르게 도망
newVelocity -= dist.normalized
* BoidSpawner.S.mouseAvoidanceDist * BoidSpawner.S.mouseAvoidanceAmt;
}
//newVelocity, newPosition은 준비됐지만 LateUpdate()가 설정할 때까지 기다림.
//이렇게 해야 다른 Boid가 새 값을 계산하기전에 현재의 Boid가 먼저 움직이지 않는다.
}
//현 Boid가 움직이기 전 모든 Boid가 자체 업데이트를 마쳐
//다른 Boid가 어찌 움직일지 결정하기 전에
//현 Boid를 움직일 때 발생할 수 있는 경합상황 예비용
void LateUpdate(){
//선형 보간을 활용, newVelocity 기준으로 현 속도 조정
velocity = (1 - BoidSpawner.S.velocityLerpAmt) * velocity
+ BoidSpawner.S.velocityLerpAmt * newVelocity;
//속도가 최소/최대 범위를 벗어나지 않게 처리
if (velocity.magnitude > BoidSpawner.S.maxVelocity) {
velocity = velocity.normalized * BoidSpawner.S.maxVelocity;
}
if (velocity.magnitude < BoidSpawner.S.minVelocity) {
velocity = velocity.normalized * BoidSpawner.S.minVelocity;
}
//새 위치 결정
newPosition = this.transform.position + velocity * Time.deltaTime;
//xz 평면으로 움직임 제한
newPosition.y = 0;
//이전 위치에서 새 위치로 방향전환
this.transform.LookAt (newPosition);
//newPosition으로 이동
this.transform.position = newPosition;
}
//이웃으로 볼 수 있을만큼 가까운 Boid 찾기
//boi는 관심 대상 보이드(Boid Of Interest)의 약자
public List GetNeighbors(Boid boi){
float closestDist = float.MaxValue; //float이 저장 가능한 최대값
Vector3 delta;
float dist;
neighbors.Clear ();
collisionRisks.Clear ();
foreach (Boid b in boids) {
if(b == boi) continue; //자신이면 패스
delta = b.transform.position - boi.transform.position;//거리 측정
dist = delta.magnitude;
if(dist < closestDist){ //가장 가까운 이웃 찾기
closestDist = dist;
closest = b;
}
//이웃으로 지정한 거리에 속하면 이웃으로 추가
if(dist < BoidSpawner.S.nearDist){
neighbors.Add (b);
}
//너무 가까운 거리에 속하면 충돌위험 그룹에 추가
if(dist < BoidSpawner.S.collisionDist){
collisionRisks.Add (b);
}
}
//이웃 없으면 가장 가까운 보이드를 이웃으로 추가
if (neighbors.Count == 0) {
neighbors.Add (closest);
}
return(neighbors); //이웃 목록 반환
}
//List<Boid>에서 이웃들 간 평균 위치 구하기
public Vector3 GetAveragePosition(List someBoids){
Vector3 sum = Vector3.zero;
foreach (Boid b in someBoids) {
sum += b.transform.position;
}
Vector3 center = sum / someBoids.Count;
return(center);
}
//List<Boid>에서 이웃들 간 평균 속도 구하기
public Vector3 GetAverageVelocity(List someBoids){
Vector3 sum = Vector3.zero;
foreach (Boid b in someBoids) {
sum += b.velocity;
}
Vector3 avg = sum / someBoids.Count;
return(avg);
}
} |