이 포스트에 있는 코드는 모두 아래 링크에 있는 튜토리얼을 기반으로 이루어졌습니다.
https://www.youtube.com/watch?v=payFhNs9hvs&list=PL4CCSwmU04MhfoJTJWA7n2AIB4dq6umeu
- Level
- Member
- Enemy
- MemberConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class Level : MonoBehaviour { public Transform memberPrefab; public Transform enemyPrefab; public int numberOfMembers; public int numberOfEnemies; public List<Member> members; public List<Enemy> enemies; public float bounds; //이동 범위 public float spawnRadius; //생성 범위 void Start () { //리스트 생성 members = new List<Member>(); enemies = new List<Enemy>(); //생성 함수 Spawn(memberPrefab, numberOfMembers); Spawn(enemyPrefab, numberOfEnemies); //리스트에 member, enemy 추가 members.AddRange(FindObjectsOfType<Member>()); enemies.AddRange(FindObjectsOfType<Enemy>()); } void Spawn(Transform prefab, int count) { //임의의 위치에 agent와 enemy 생성하기 for (int i = 0; i < count; i++) { Instantiate(prefab, new Vector3(Random.Range(-spawnRadius, spawnRadius), Random.Range(-spawnRadius, spawnRadius), 0), Quaternion.identity); } } //이웃을 분류하는 리스트 public List<Member> GetNeighbors(Member member, float radius) { //이웃을 반환할 리스트 생성 List<Member> neighborsFound = new List<Member>(); //members리스트 안에 있는 member중 하나를 othermember라는 변수로 지정 foreach(Member otherMember in members) { if (otherMember == member) continue; //조건 성립하면 다시 돌아감 //본인의 위치가 othermember와 일정 거리 가까우면 if(Vector3.Distance(member.position, otherMember.position) <= radius) { //neighborsFound 리스트에 otherMember를 추가 neighborsFound.Add(otherMember); } } return neighborsFound; //neighborsFound 반환 } //Enemy를 분류하는 리스트 public List<Enemy> GetEnemies(Member member, float radius) { //enemy를 반환할 리스트 생성 List<Enemy> returnEnemies = new List<Enemy>(); //enemies리스트 안에 있는 enemy중 하나를 enemy라는 변수로 지정 foreach (Enemy enemy in enemies) { //본인의 위치와 enemy의 위치가 일정 거리 가까우면 if(Vector3.Distance(member.position, enemy.transform.position) <= radius) { //returnEnemies 리스트에 enemy를 추가 returnEnemies.Add(enemy); } } return returnEnemies; //returnEnemies 반환 } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class Member : MonoBehaviour { public Vector3 position; public Vector3 velocity; public Vector3 acceleration; public Level level; public MemberConfig conf; Vector3 wanderTarget; private void Start() { position = transform.position; //FindObjectOfType은 getComponent와 유사하지만 전역적으로 component를 찾아온다 level = FindObjectOfType<Level>(); conf = FindObjectOfType<MemberConfig>(); position = transform.position; velocity = new Vector3(Random.Range(-3, 3), Random.Range(-3, 3), 0); } private void Update() { acceleration = Combine(); //이동함수 호출 acceleration = Vector3.ClampMagnitude(acceleration, conf.maxAccelaeration);//ClampMagnitude: max값에 도달하면 그 이후의 값은 max로 반환(상한선) velocity = velocity + acceleration * Time.deltaTime; //속도 velocity = Vector3.ClampMagnitude(velocity, conf.maxVelocity); position = position + velocity * Time.deltaTime; //위치 WrapAround(ref position, -level.bounds, level.bounds); //범위 벗어날 경우 //이동방향으로 회전 transform.rotation = Quaternion.LookRotation(position - transform.position); //위치 할당 transform.position = position; } //이동 함수1 - 모였다 퍼졌다 protected Vector3 Wander() { //이동 크기 float jitter = conf.wanderJitter * Time.deltaTime; //jitter(진폭)변수 생성 wanderTarget += new Vector3(0, RandomBinomial() * jitter, 0); //랜덤이항식*jitter으로 이동 wanderTarget = wanderTarget.normalized; //정규화: 방향에 상관없이 똑같은 크기를 받도록 wanderTarget *= conf.wanderRadius; //?? 이동 지름을 곱해줌 //위치 좌표 Vector3 targetInLocalSpace = wanderTarget + new Vector3(conf.wanderDistance, conf.wanderDistance, 0); //타겟이 로컬좌표(상대위치)에 있는, ??wanderDistance변수만큼 y축으로 더해줌 Vector3 targetInWorldSpace = transform.TransformPoint(targetInLocalSpace); //월드좌표(절대위치), transformpoint: local좌표 -> world좌표로 변환 targetInLocalSpace -= this.position; //방향 return targetInLocalSpace.normalized; //로컬좌표타겟 변수 정규화 } //이동 함수2 - wander함수보다 더 랜덤하게 이동 Vector3 Cohesion() { Vector3 cohesionVector = new Vector3(); int countMembers = 0; var neighbors = level.GetNeighbors(this, conf.cohesionRadius); if (neighbors.Count == 0) return cohesionVector; //??neighbors리스트에 있는 member에 관한 반복문 foreach(var member in neighbors) { //isInFOV: 시야각 공식 if (isInFOV(member.position)) { cohesionVector += member.position; countMembers++; } } if (countMembers == 0) return cohesionVector; //?? cohesionVector /= countMembers; cohesionVector = cohesionVector - this.position; cohesionVector = Vector3.Normalize(cohesionVector); return cohesionVector; } //alignment(회전) - 이웃과 속도(벡터값 - 크기와 방향 가짐)를 맞추는 함수 Vector3 Alignment() { Vector3 alignVector = new Vector3(); //새로운 변수 공간 생성 var members = level.GetNeighbors(this, conf.alignmentRadius); //member로 현재 스크립트가 적용된 gameobject를 가져온다, conf.alignmentRadius는 alignment 적용거리의 범위(collider)이다 if (members.Count == 0) return alignVector; // 자신의 시야각에 멤버가 존재하면 정렬을 맞춤 foreach (var member in members) { //만약 시야각에 member가 들어오면, alignVector에 시야각에 들어온 member의 벡터값을 더해준다 if (isInFOV(member.position)) alignVector += member.velocity; //alignVector에 member의 속도(벡터값)를 더해주는 } return alignVector.normalized; } //separation - 이웃과 일정한 거리를 띄우는 Vector3 Separation() { Vector3 separteVector = new Vector3(); var members = level.GetNeighbors(this, conf.separationRadius); if (members.Count == 0) return separteVector; foreach(var member in members) { if(isInFOV(member.position)) { Vector3 movingTowards = this.position - member.position; //member에서 멀어지는 자신의 방향 if(movingTowards.magnitude > 0)//만약 자신이 member와 접촉되지 않았다면 { separteVector += movingTowards.normalized / movingTowards.magnitude; //1의 값(정규화 값)을 기준으로 자신의 원래 스칼라 값을 나누어주어서 이동해야되는 크기에 따라 속도를 정의한다 ,magnitude: 벡터의 길이(크기) } } } return separteVector.normalized; } Vector3 Avoidance() { Vector3 avoidVector = new Vector3(); var enemyList = level.GetEnemies(this, conf.avoidanceRadius); //level스크립트에서 enemy리스트 가져오기 if (enemyList.Count == 0) return avoidVector; foreach(var enemy in enemyList) { avoidVector += RunAway(enemy.transform.position); //적과 반대로 이동하는 } return avoidVector.normalized; } Vector3 RunAway(Vector3 target) { Vector3 neededVelocity = (position - target).normalized * conf.maxVelocity; //적(target)의 위치와 반대방향으로 이동 return neededVelocity - velocity; //적을 피해 이동하는 벡터 - member가 원래 가야되는 } //최종 조합 - wander() + cohesion() + alignment() + separation() + avoidance()의 결합 (snake game처럼 보이는) virtual protected Vector3 Combine() { Vector3 finalVec = conf.cohesionPriority * Cohesion() + conf.wanderPriority * Wander() + conf.alignmentPriority * Alignment() + conf.separationPriority * Separation() + conf.avoidancePriority * Avoidance(); return finalVec; } //agent가 범위를 벗어나갈 때 초기값으로 이동시키는 함수 void WrapAround(ref Vector3 vector, float min, float max) { //wrapAroundfloat이라는 공식을 반환함 vector.x = WrapAroundFloat(vector.x, min, max); vector.y = WrapAroundFloat(vector.y, min, max); vector.z = 0;//WrapAroundFloat(vector.z, min, max); //2D, 3D } //wrapAroundFloat이라는 공식 float WrapAroundFloat(float value, float min, float max) { if (value > max) value = min; else if (value < min) value = max; return value; } //랜덤 매개변수 이항식 뺄셈 float RandomBinomial() { //random은 c언어에서는 0부터 1값만 나온다 //return Random.Range(0f, 1f) - Random.Range(0f, 1f); return Random.Range(-1.0f, 1.0f); } //fov: 시야각 - '속도'와 '(변수)-현재 위치' 사이의 각도의 반환값이 conf.maxFOV보다 작을 때 bool isInFOV(Vector3 vec) { return Vector3.Angle(this.velocity, vec - this.position) <= conf.maxFOV; } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class Enemy : MonoBehaviour { //member스크립트 상속 private RaycastHit hit; private Vector3 movePos; private void Start() { movePos = transform.position; } private void Update() { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out hit, 10000)) { movePos = hit.point; } //sqrMagnitude는 벡터의 길이의 제곱한 값을 반환.루트 연산을 하지 않기 때문에 Magnitude보다 속도가 훨씬 빠르다 if ((movePos - transform.position).sqrMagnitude >= 0.2f * 0.2f) { Quaternion rot = Quaternion.LookRotation(hit.point - transform.position); transform.rotation = Quaternion.Slerp(transform.rotation, rot, Time.deltaTime * 10.0f); transform.Translate(Vector3.forward * Time.deltaTime * 10.0f); } } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class MemberConfig : MonoBehaviour { public float maxFOV = 180; //시야각 public float maxAccelaeration; //가속도 public float maxVelocity; //속도(방향 + 속력) //Wander Varaibles public float wanderJitter; //진폭 public float wanderRadius; //적용 범위 public float wanderDistance; //거리 public float wanderPriority; //가중치(weight) //Cohesion Varaibles public float cohesionRadius; public float cohesionPriority; //Alignment Varaibles public float alignmentRadius; public float alignmentPriority; //Separation Varaibles public float separationRadius; public float separationPriority; //Avoidance Varaibles public float avoidanceRadius; public float avoidancePriority; } | cs |
'Project > Galapagos' 카테고리의 다른 글
마우스를 따라서 이동하는 개체 코드 (0) | 2019.01.11 |
---|---|
*클래스 타입이 list인 코드 분석 (0) | 2019.01.11 |
네트워크 Day5 (포톤네트워크 PUN2 - 동기화 분석) (0) | 2018.12.25 |
네트워크 Day4 (포톤네트워크 PUN2 - Cross Device) (0) | 2018.12.18 |
유체역학 이론 Day01(구간 반복 시키기 - Bound Movement) (0) | 2018.12.14 |