FlockChild.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. /**************************************
  2. Copyright Unluck Software
  3. www.chemicalbliss.com
  4. ***************************************/
  5. using UnityEngine;
  6. public class FlockChild:MonoBehaviour{
  7. [HideInInspector]
  8. public FlockController _spawner; //Reference to the flock controller that spawned this bird
  9. [HideInInspector]
  10. public Vector3 _wayPoint; //Waypoint used to steer towards
  11. public float _speed; //Current speed of bird
  12. [HideInInspector]
  13. public bool _dived =true; //Indicates if this bird has recently performed a dive movement
  14. [HideInInspector]
  15. public float _stuckCounter; //prevents looping around a waypoint by increasing minimum distance to waypoint
  16. [HideInInspector]
  17. public float _damping; //Damping used for steering (steer speed)
  18. [HideInInspector]
  19. public bool _soar = true; // Indicates if this is soaring
  20. [HideInInspector]
  21. public bool _landing; // Indicates if bird is landing or sitting idle
  22. [HideInInspector]
  23. public float _targetSpeed; // Max bird speed
  24. [HideInInspector]
  25. public bool _move = true; // Indicates if bird can fly
  26. public GameObject _model; // Reference to bird model
  27. public Transform _modelT; // Reference to bird model transform (caching tranform to avoid any extra getComponent calls)
  28. [HideInInspector]
  29. public float _avoidValue; //Random value used to check for obstacles. Randomized to lessen uniformed behaviour when avoiding
  30. [HideInInspector]
  31. public float _avoidDistance; //How far from an obstacle this can be before starting to avoid it
  32. float _soarTimer;
  33. bool _instantiated;
  34. static int _updateNextSeed = 0;
  35. int _updateSeed = -1;
  36. [HideInInspector]
  37. public bool _avoid = true;
  38. public Transform _thisT; //Reference to the transform component
  39. public Vector3 _landingPosOffset;
  40. public void Start(){
  41. FindRequiredComponents(); //Check if references to transform and model are set (These should be set in the prefab to avoid doind this once a bird is spawned, click "Fill" button in prefab)
  42. Wander(0.0f);
  43. SetRandomScale();
  44. _thisT.position = findWaypoint();
  45. RandomizeStartAnimationFrame();
  46. InitAvoidanceValues();
  47. _speed = _spawner._minSpeed;
  48. _spawner._activeChildren++;
  49. _instantiated = true;
  50. if(_spawner._updateDivisor > 1){
  51. int _updateSeedCap = _spawner._updateDivisor -1;
  52. _updateNextSeed++;
  53. this._updateSeed = _updateNextSeed;
  54. _updateNextSeed = _updateNextSeed % _updateSeedCap;
  55. }
  56. }
  57. public void Update() {
  58. //Skip frames
  59. if (_spawner._updateDivisor <=1 || _spawner._updateCounter == _updateSeed){
  60. SoarTimeLimit();
  61. CheckForDistanceToWaypoint();
  62. RotationBasedOnWaypointOrAvoidance();
  63. LimitRotationOfModel();
  64. }
  65. }
  66. public void OnDisable() {
  67. CancelInvoke();
  68. _spawner._activeChildren--;
  69. }
  70. public void OnEnable() {
  71. if(_instantiated){
  72. _spawner._activeChildren++;
  73. if(_landing){
  74. _model.GetComponent<Animation>().Play(_spawner._idleAnimation);
  75. }else{
  76. _model.GetComponent<Animation>().Play(_spawner._flapAnimation);
  77. }
  78. }
  79. }
  80. public void FindRequiredComponents(){
  81. if(_thisT == null) _thisT = transform;
  82. if(_model == null) _model = _thisT.Find("Model").gameObject;
  83. if(_modelT == null) _modelT = _model.transform;
  84. }
  85. public void RandomizeStartAnimationFrame(){
  86. foreach(AnimationState state in _model.GetComponent<Animation>()) {
  87. state.time = Random.value * state.length;
  88. }
  89. }
  90. public void InitAvoidanceValues(){
  91. _avoidValue = Random.Range(.3f, .1f);
  92. if(_spawner._birdAvoidDistanceMax != _spawner._birdAvoidDistanceMin){
  93. _avoidDistance = Random.Range(_spawner._birdAvoidDistanceMax , _spawner._birdAvoidDistanceMin);
  94. return;
  95. }
  96. _avoidDistance = _spawner._birdAvoidDistanceMin;
  97. }
  98. public void SetRandomScale(){
  99. float sc = Random.Range(_spawner._minScale, _spawner._maxScale);
  100. _thisT.localScale=new Vector3(sc,sc,sc);
  101. }
  102. //Soar Timeout - Limits how long a bird can soar
  103. public void SoarTimeLimit(){
  104. if(this._soar && _spawner._soarMaxTime > 0){
  105. if(_soarTimer > _spawner._soarMaxTime){
  106. this.Flap();
  107. _soarTimer = 0.0f;
  108. }else {
  109. _soarTimer+=_spawner._newDelta;
  110. }
  111. }
  112. }
  113. public void CheckForDistanceToWaypoint(){
  114. if(!_landing && (_thisT.position - _wayPoint).magnitude < _spawner._waypointDistance+_stuckCounter){
  115. Wander(0.0f);
  116. _stuckCounter=0.0f;
  117. }else if(!_landing){
  118. _stuckCounter+=_spawner._newDelta;
  119. }else{
  120. _stuckCounter=0.0f;
  121. }
  122. }
  123. public void RotationBasedOnWaypointOrAvoidance(){
  124. Vector3 lookit = _wayPoint - _thisT.position;
  125. if(_targetSpeed > -1 && lookit != Vector3.zero){
  126. Quaternion rotation = Quaternion.LookRotation(lookit);
  127. _thisT.rotation = Quaternion.Slerp(_thisT.rotation, rotation, _spawner._newDelta * _damping);
  128. }
  129. if(_spawner._childTriggerPos){
  130. if((_thisT.position - _spawner._posBuffer).magnitude < 1){
  131. _spawner.SetFlockRandomPosition();
  132. }
  133. }
  134. _speed = Mathf.Lerp(_speed, _targetSpeed, _spawner._newDelta* 2.5f);
  135. //Position forward based on object rotation
  136. if(_move){
  137. _thisT.position += _thisT.forward*_speed*_spawner._newDelta;
  138. if(_avoid && _spawner._birdAvoid)
  139. Avoidance();
  140. }
  141. }
  142. public bool Avoidance() {
  143. RaycastHit hit = new RaycastHit();
  144. Vector3 fwd = _modelT.forward;
  145. bool r = false;
  146. Quaternion rot = Quaternion.identity;
  147. Vector3 rotE = Vector3.zero;
  148. Vector3 pos = Vector3.zero;
  149. pos = _thisT.position;
  150. rot = _thisT.rotation;
  151. rotE = _thisT.rotation.eulerAngles;
  152. if (Physics.Raycast(_thisT.position, fwd+(_modelT.right*_avoidValue), out hit, _avoidDistance, _spawner._avoidanceMask)){
  153. rotE.y -= _spawner._birdAvoidHorizontalForce*_spawner._newDelta*_damping;
  154. rot.eulerAngles = rotE;
  155. _thisT.rotation = rot;
  156. r= true;
  157. }else if (Physics.Raycast(_thisT.position,fwd+(_modelT.right*-_avoidValue), out hit, _avoidDistance, _spawner._avoidanceMask)){
  158. rotE.y += _spawner._birdAvoidHorizontalForce*_spawner._newDelta*_damping;
  159. rot.eulerAngles = rotE;
  160. _thisT.rotation = rot;
  161. r= true;
  162. }
  163. if (_spawner._birdAvoidDown && !this._landing && Physics.Raycast(_thisT.position, -Vector3.up, out hit, _avoidDistance, _spawner._avoidanceMask)){
  164. rotE.x -= _spawner._birdAvoidVerticalForce*_spawner._newDelta*_damping;
  165. rot.eulerAngles = rotE;
  166. _thisT.rotation = rot;
  167. pos.y += _spawner._birdAvoidVerticalForce*_spawner._newDelta*.01f;
  168. _thisT.position = pos;
  169. r= true;
  170. }else if (_spawner._birdAvoidUp && !this._landing && Physics.Raycast(_thisT.position, Vector3.up, out hit, _avoidDistance, _spawner._avoidanceMask)){
  171. rotE.x += _spawner._birdAvoidVerticalForce*_spawner._newDelta*_damping;
  172. rot.eulerAngles = rotE;
  173. _thisT.rotation = rot;
  174. pos.y -= _spawner._birdAvoidVerticalForce*_spawner._newDelta*.01f;
  175. _thisT.position = pos;
  176. r= true;
  177. }
  178. return r;
  179. }
  180. public void LimitRotationOfModel(){
  181. Quaternion rot = Quaternion.identity;
  182. Vector3 rotE = Vector3.zero;
  183. rot = _modelT.localRotation;
  184. rotE = rot.eulerAngles;
  185. if((_soar && _spawner._flatSoar|| _spawner._flatFly && !_soar)&& _wayPoint.y > _thisT.position.y||_landing){
  186. rotE.x = Mathf.LerpAngle(_modelT.localEulerAngles.x, -_thisT.localEulerAngles.x, _spawner._newDelta * 1.75f);
  187. rot.eulerAngles = rotE;
  188. _modelT.localRotation = rot;
  189. }else{
  190. rotE.x = Mathf.LerpAngle(_modelT.localEulerAngles.x, 0.0f, _spawner._newDelta * 1.75f);
  191. rot.eulerAngles = rotE;
  192. _modelT.localRotation = rot;
  193. }
  194. }
  195. public void Wander(float delay){
  196. if(!_landing){
  197. _damping = Random.Range(_spawner._minDamping, _spawner._maxDamping);
  198. _targetSpeed = Random.Range(_spawner._minSpeed, _spawner._maxSpeed);
  199. Invoke("SetRandomMode", delay);
  200. }
  201. }
  202. public void SetRandomMode(){
  203. CancelInvoke("SetRandomMode");
  204. if(!_dived && Random.value < _spawner._soarFrequency){
  205. Soar();
  206. }else if(!_dived && Random.value < _spawner._diveFrequency){
  207. Dive();
  208. }else{
  209. Flap();
  210. }
  211. }
  212. public void Flap(){
  213. if(_move){
  214. if(this._model != null) _model.GetComponent<Animation>().CrossFade(_spawner._flapAnimation, .5f);
  215. _soar=false;
  216. animationSpeed();
  217. _wayPoint = findWaypoint();
  218. _dived = false;
  219. }
  220. }
  221. public Vector3 findWaypoint(){
  222. Vector3 t = Vector3.zero;
  223. t.x = Random.Range(-_spawner._spawnSphere, _spawner._spawnSphere) + _spawner._posBuffer.x;
  224. t.z = Random.Range(-_spawner._spawnSphereDepth, _spawner._spawnSphereDepth) + _spawner._posBuffer.z;
  225. t.y = Random.Range(-_spawner._spawnSphereHeight, _spawner._spawnSphereHeight) + _spawner._posBuffer.y;
  226. return t;
  227. }
  228. public void Soar(){
  229. if(_move){
  230. _model.GetComponent<Animation>().CrossFade(_spawner._soarAnimation, 1.5f);
  231. _wayPoint= findWaypoint();
  232. _soar = true;
  233. }
  234. }
  235. public void Dive(){
  236. if(_spawner._soarAnimation!=null){
  237. _model.GetComponent<Animation>().CrossFade(_spawner._soarAnimation, 1.5f);
  238. }else{
  239. foreach(AnimationState state in _model.GetComponent<Animation>()) {
  240. if(_thisT.position.y < _wayPoint.y +25){
  241. state.speed = 0.1f;
  242. }
  243. }
  244. }
  245. _wayPoint= findWaypoint();
  246. _wayPoint.y -= _spawner._diveValue;
  247. _dived = true;
  248. }
  249. public void animationSpeed(){
  250. foreach(AnimationState state in _model.GetComponent<Animation>()) {
  251. if(!_dived && !_landing){
  252. state.speed = Random.Range(_spawner._minAnimationSpeed, _spawner._maxAnimationSpeed);
  253. }else{
  254. state.speed = _spawner._maxAnimationSpeed;
  255. }
  256. }
  257. }
  258. }