Subversion Repository Public Repository

Nextrek

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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
// Asteroid Field C# Script (version: 1.03)
// SPACE UNITY - Space Scene Construction Kit
// http://www.spaceunity.com
// (c) 2013 Stefan Persson

// DESCRIPTION:
// This script creates a localized asteroid field around itself. As the object moves
// the asteroids will optionally re-spawn out of range asteroids within range (but out of sight.)

// INSTRUCTIONS:
// Use the AsteroidField prefab and make it a child of an object you wish to spawn asteroids around (e.g. a space ship)
// Alternatively, drag this script onto the game object that should be the center of the asteroid field.

// PARAMETERS:
//   rotationSpeed	(rotational speed of the asteroid)
//   driftSpeed		(drift/movement speed of the asteroid)

// Version History
// 1.03 - Added compiler conditional code for major versions 4.1, 4.2, 4.3
//      - Changed transparent asteroid material to new shader SpaceUnity/AsteroidTransparent located
//			in a Resources subfolder to ensure it is included during compile (before, transparent asteroids
//			wouldn't render in 4.x since the shader was not included in the build)
// 1.02 - Prefixed with SU_AsteroidField to avoid naming conflicts.
//        Added documentation.
// 1.01 - Initial Release.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class SU_AsteroidField : MonoBehaviour {	
	// Poly Count (quality) of the asteroids in the field
	public SU_Asteroid.PolyCount polyCount = SU_Asteroid.PolyCount.HIGH;
	// Poly Count (quality) of the asteroid colliders (LOW = fast, HIGH = slow)
	public SU_Asteroid.PolyCount polyCountCollider = SU_Asteroid.PolyCount.LOW;	

	// Array of prefabs that the asteroid fields should consist of
	public Transform[] prefabAsteroids;
	
	// Arrays of materials of asteroids in the field
	public Material[] materialVeryCommon;		// 50%
	public Material[] materialCommon;			// 30%
	public Material[] materialRare;				// 15%
	public Material[] materialVeryRare;			// 5%
			
	// Range of asteroid field sphere (when asteroids are beyond this range from the game object  
	// they will respawn (relocate) to within range at distanceSpawn of range.
	public float range = 20000.0f;
	// Maximum number of asteroids in the sphere (configure to your needs for look and performance)
	public int maxAsteroids;	
	// Respawn destroyed asteroids true/false
	public bool respawnDestroyedAsteroids = true;
	// Respawn if out of range (must be true for infinite/endless asteroid fields
	public bool respawnIfOutOfRange = true;
	// Distance percentile of range to relocate/spawn asteroids to
	public float distanceSpawn = 0.95f;		
	// Minimum scale of asteroid	
	public float minAsteroidScale = 0.1f;
	// Maximum scale of asteroid
	public float maxAsteroidScale = 1.0f;	
	// Multiplier of scale
	public float scaleMultiplier = 1.0f;
		
	// Is rigid body or not
	public bool isRigidbody = false;
	
	// NON-RIGIDBODY ASTEROIDS ---
	// Minimum rotational speed of asteroid
	public float minAsteroidRotationSpeed = 0.0f;
	// Maximum rotational speed of asteroid
	public float maxAsteroidRotationSpeed = 1.0f;
	// Rotation speed multiplier
	public float rotationSpeedMultiplier = 1.0f;
	// Minimum drift/movement speed of asteroid
	public float minAsteroidDriftSpeed = 0.0f;
	// Maximum drift/movement speed of asteroid
	public float maxAsteroidDriftSpeed = 1.0f;
	// Multiplier of driftSpeed
	public float driftSpeedMultiplier = 1.0f;
	// ---------------------------
	
	// RIGIDBODY ASTEROIDS -------
	// Mass of asteroid (scaled between minAsterodiScale/maxAsteroidScale)
	public float mass = 1.0f;
	// Minimum angular velocity of asteroid (rotational speed)
	public float minAsteroidAngularVelocity = 0.0f;
	// Maximum angular velocity of asteroid (rotational speed)
	public float maxAsteroidAngularVelocity = 1.0f;
	// Angular velocity (rotational speed) multiplier
	public float angularVelocityMultiplier = 1.0f;
	// Minimum velocity of asteroid (drift/movement speed)
	public float minAsteroidVelocity = 0.0f;
	// Maximum velocity of asteroid (drift/movement speed)
	public float maxAsteroidVelocity = 1.0f;	
	// Velocity (drift/movement speed) multiplier
	public float velocityMultiplier = 1.0f;	
	// ----------------------------
	
	// Fade asteroids in/out of range
	public bool fadeAsteroids = true;
	// Distance percentile of spawn distance to start fading asteroids
	// Alpha = 1.0 at distanceFade*distanceSpawn*range, and 0.0 at distanceSpawn*range
	public float distanceFade = 0.95f;
	
	// Private variables
	private float _distanceToSpawn;	
	private float _distanceToFade;
	private Transform _cacheTransform;	
	private List<Transform> _asteroids = new List<Transform>();
	private List<Material> _asteroidsMaterials = new List<Material>();
	private List<bool> _asteroidsFading = new List<bool>();
	private Hashtable _materialsTransparent = new Hashtable();
	private SortedList<int, string> _materialList = new SortedList<int, string>(4);
	
	
	void OnEnable () {
		// Cache reference to transform to increase performance
		_cacheTransform = transform;			
		
		// Calculate the actual spawn and fade distances
		_distanceToSpawn = range * distanceSpawn;
		_distanceToFade = range * distanceSpawn * distanceFade;
		
		// If fading is enabled...
		if (fadeAsteroids && _materialsTransparent.Count == 0) {	
			// Create transparent materials to be used when in fading range
			CreateTransparentMaterial(materialVeryCommon, _materialsTransparent);
			CreateTransparentMaterial(materialCommon, _materialsTransparent);
			CreateTransparentMaterial(materialRare, _materialsTransparent);
			CreateTransparentMaterial(materialVeryRare, _materialsTransparent);
		}
		
		// Populate the material list based on probabilty of materials
		if (_materialList.Count == 0) {
			if (materialVeryRare.Length > 0) _materialList.Add(5, "VeryRare");
			if (materialRare.Length > 0) _materialList.Add(5 + 15, "Rare");
			if (materialCommon.Length > 0) _materialList.Add(5 + 15 + 30, "Common");				
			if (materialVeryCommon.Length != 0) { 
				_materialList.Add(5 + 15 + 30 + 50, "VeryCommon");			
			} else {
				Debug.LogError("Asteroid Field must have at least one Material in the 'Material Very Common' Array."); 
			}		
		}
		
		// Check if there are any asteroid objects that was spawned prior to this script being disabled
		// If there are asteroids in the list, activate the gameObject again.
		for (int i = 0; i < _asteroids.Count; i++) {
			#if UNITY_3_5
				_asteroids[i].gameObject.active = true;		
			#endif
			#if UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3
				_asteroids[i].gameObject.SetActive(true);
			#endif
		}
		
		// Spawn new asteroids in the entire sphere (not just at spawn range, hence the "false" parameter)
		SpawnAsteroids(false);
	}
	
	void OnDisable () {
		// Asteroid field game object has been disabled, disable all the asteroids as well
		for (int i = 0; i < _asteroids.Count; i++) {
			// If the transform of the asteroid exists (it won't be upon application exit for example)...			
			if (_asteroids[i] != null) {
				// deactivate the asteroid gameObject
			#if UNITY_3_5
				_asteroids[i].gameObject.active = false;		
			#endif
			#if UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3
				_asteroids[i].gameObject.SetActive(false);
			#endif
			}
		}
	}		
	
	void OnDrawGizmosSelected () {
	    // Draw a yellow wire gizmo sphere at the transform's position with the size of the asteroid field
	    Gizmos.color = Color.yellow;
	    Gizmos.DrawWireSphere (transform.position, range);
	}	
	
	void Update () {
		// Iterate through asteroids and relocate them as parent object moves		
		for (int i = 0; i < _asteroids.Count; i++) {
			// Cache the reference to the Transform of the asteroid in the list
			Transform _asteroid = _asteroids[i];
			
			// If the asteroid in the list has a Transform...
			if (_asteroid != null) {				
				// Calculate the distance of the asteroid to the center of the asteroid field
				float _distance = Vector3.Distance(_asteroid.position, _cacheTransform.position);
				
				// If the distance is greater than the range variable...
				if (_distance > range && respawnIfOutOfRange) {
					// Relocate ("respawn") the asteroid to a new position at spawning distance					
					_asteroid.position = Random.onUnitSphere * _distanceToSpawn + _cacheTransform.position;
					// Give the asteroid a new scale within the min/max scale range
					float _newScale = Random.Range(minAsteroidScale,maxAsteroidScale) * scaleMultiplier;
					_asteroid.localScale = new Vector3(_newScale, _newScale, _newScale);
					// Give the asteroid a new random rotation
					Vector3 _newRotation = new Vector3(Random.Range(0,360), Random.Range(0,360), Random.Range(0,360));					
					_asteroid.eulerAngles = _newRotation;
					// Recalculate the distance since it has been relocated
					//_distance = Vector3.Distance(_asteroid.position, _cacheTransform.position);					
				}				
				
				// If fading of asteroids is enabled...
				if (fadeAsteroids) {
					// ...and if distance of asteroid is greater than distance to fade...
					if (_distance > _distanceToFade) {
						// ...and if the bool flag has been not been set...
						if (!_asteroidsFading[i]) {
							// Change the material of the asteroid to a transparent material that supports alpha fading
							_asteroid.GetComponent<Renderer>().sharedMaterial = (Material) _materialsTransparent[_asteroidsMaterials[i]];
							// Set the fading flag to true (we compare a Bool list instead of costly checking the material in each frame
							_asteroidsFading[i] = true;
						}
						// Cache the color of the material
						
						Color _col = _asteroid.GetComponent<Renderer>().material.color;
						// Calculate the new alpha value between _distanceToFade (1.0) and _distanceToSpawn (0.0)
						float _alpha = Mathf.Clamp01(1.0f - ((_distance - _distanceToFade) / (_distanceToSpawn - _distanceToFade)));
						// Set the same color with the new alpha value to the asteroid
						_asteroid.GetComponent<Renderer>().material.color = new Color(_col.r, _col.g, _col.b, _alpha );
					} else {
						// The asteroid is within range, and if the fading flag is still set...
						if (_asteroidsFading[i]) {					
							// Change the material back to the original non-transparent material
							_asteroid.GetComponent<Renderer>().material = (Material) _asteroidsMaterials[i];
							// Set the fading falg to false to prevent material changes each frame
							_asteroidsFading[i] = false;
						}
					}
				}
			} else {
				// Asteroid transform must have been been destroyed for some reason (from another scriåt?), remove it from the lists
				_asteroids.RemoveAt(i);
				_asteroidsMaterials.RemoveAt(i);
				_asteroidsFading.RemoveAt(i);
			}
			
			// 	If respawning is enabled and asteroid count is lower than Max Asteroids...		
			if (respawnDestroyedAsteroids && _asteroids.Count < maxAsteroids) {
				// Spawn new asteroids (where true states that they are to be spawned at spawn distance rather than anywhere in range)
				SpawnAsteroids(true);
			}
		}
		
				
	}
	
	
	/// <summary>
	/// Spawns the number of asteroids needed to reach maxAsteroids
	/// </summary>
	/// <param name='atSpawnDistance'>
	/// true = spawn on sphere at distanceSpawn * range (used for respawning asteroids)
	/// false = spawn in sphere within distanceSpawn * range (used for brand new asteroid fields)
	/// </param>
	void SpawnAsteroids(bool atSpawnDistance) {
		// Spawn new asteroids at a distance if count is below maxAsteroids (e.g. asteroids were destroyed outside of this script)
		while (_asteroids.Count < maxAsteroids) {
			// Select a random asteroid from the prefab array			
			Transform _newAsteroidPrefab = prefabAsteroids[Random.Range(0, prefabAsteroids.Length)];
			
			Vector3 _newPosition = Vector3.zero;			
			if (atSpawnDistance) {
				// Spawn asteroid at spawn distance (this is used for existing asteroid fields so it spawns out of visible range)
				_newPosition = _cacheTransform.position + Random.onUnitSphere * _distanceToSpawn;
			} else {
				// Spawn asteroid anywhere within range (this is used for new asteroid fields before it becomes visible)
				_newPosition = _cacheTransform.position + Random.insideUnitSphere * _distanceToSpawn;
			}
			
			// Instantiate the new asteroid at a random location
			Transform _newAsteroid = (Transform) Instantiate(_newAsteroidPrefab, _newPosition, _cacheTransform.rotation);
			
			// Set a random material of the asteroid based on the weighted probabilty list
			switch (WeightedRandom(_materialList)) {
			case "VeryCommon":
				_newAsteroid.GetComponent<Renderer>().sharedMaterial = materialVeryCommon[Random.Range(0, materialVeryCommon.Length)];
				break;
			case "Common":
				_newAsteroid.GetComponent<Renderer>().sharedMaterial = materialCommon[Random.Range(0, materialCommon.Length)];
				break;
			case "Rare":
				_newAsteroid.GetComponent<Renderer>().sharedMaterial= materialRare[Random.Range(0, materialRare.Length)];
				break;
			case "VeryRare":
				_newAsteroid.GetComponent<Renderer>().sharedMaterial = materialVeryRare[Random.Range(0, materialVeryRare.Length)];
				break;
			}
			
			// Add the asteroid to a list used to keep track of them
			_asteroids.Add(_newAsteroid);
			// A list to store which material each asteroid has
			_asteroidsMaterials.Add(_newAsteroid.GetComponent<Renderer>().sharedMaterial);
			// Improve performance by having a list of bool values whether or not asteroids are fading
			_asteroidsFading.Add(false);
			
			// If the asteroid has the Asteroid script attached to it...
			if (_newAsteroid.GetComponent<SU_Asteroid>() != null) {
				// Set the mesh of the asteroid based on chosen polycount
				_newAsteroid.GetComponent<SU_Asteroid>().SetPolyCount(polyCount);
				// If the asteroid has a collider...
				if (_newAsteroid.GetComponent<Collider>() != null) {
					_newAsteroid.GetComponent<SU_Asteroid>().SetPolyCount(polyCountCollider, true);
				}
			}
			
			// Set scale of asteroid within min/max scale * scaleMultiplier
			float _newScale = Random.Range(minAsteroidScale,maxAsteroidScale) * scaleMultiplier;
			_newAsteroid.localScale = new Vector3(_newScale, _newScale, _newScale);
			
			// Set a random orientation of the asteroid			
			_newAsteroid.eulerAngles = new Vector3(Random.Range(0,360), Random.Range(0,360), Random.Range(0,360));			
			
			if (isRigidbody) {
				// RIGIDBODY ASTEROIDS
				// If the asteroid prefab has a rigidbody...
				if (_newAsteroid.GetComponent<Rigidbody>() != null) {
					// Set the mass to mass specified in AsteroidField mutiplied by scale
					_newAsteroid.GetComponent<Rigidbody>().mass = mass * _newScale;
					// Set the velocity (speed) of the rigidbody to within the min/max velocity range multiplier by velocityMultiplier
					_newAsteroid.GetComponent<Rigidbody>().velocity = _newAsteroid.transform.forward * Random.Range(minAsteroidVelocity, maxAsteroidVelocity) * velocityMultiplier;
					// Set the angular velocity (rotational speed) of the rigidbody to within the min/max velocity range multiplier by velocityMultiplier
					_newAsteroid.GetComponent<Rigidbody>().angularVelocity = new Vector3(Random.Range(0.0f,1.0f), Random.Range(0.0f,1.0f), Random.Range(0.0f,1.0f)) * Random.Range(minAsteroidAngularVelocity, maxAsteroidAngularVelocity) * angularVelocityMultiplier;
				} else {
					Debug.LogWarning("AsteroidField is set to spawn rigidbody asterodids but one or more asteroid prefabs do not have rigidbody component attached.");
				}
			} else {
				// NON-RIGIDBODY ASTEROIDS
				
				// If the asteroid prefab has a rigidbody...
				if (_newAsteroid.GetComponent<Rigidbody>() != null) {
					// Destroy the rigidbody since the asteroid field is spawning non-rigidbody asteroids
					Destroy(_newAsteroid.GetComponent<Rigidbody>());
				}
				// If the asteroid has the Asteroid script attached to it...
				if (_newAsteroid.GetComponent<SU_Asteroid>() != null) {
					// Set rotation and drift axis and speed				
					_newAsteroid.GetComponent<SU_Asteroid>().rotationSpeed = Random.Range(minAsteroidRotationSpeed, maxAsteroidRotationSpeed);
					_newAsteroid.GetComponent<SU_Asteroid>().rotationalAxis = new Vector3(Random.Range(0.0f,1.0f), Random.Range(0.0f,1.0f), Random.Range(0.0f,1.0f));
					_newAsteroid.GetComponent<SU_Asteroid>().driftSpeed = Random.Range(minAsteroidDriftSpeed, maxAsteroidDriftSpeed);
					_newAsteroid.GetComponent<SU_Asteroid>().driftAxis = new Vector3(Random.Range(0.0f,1.0f), Random.Range(0.0f,1.0f), Random.Range(0.0f,1.0f));
				}
				
			}
		}	
		
	}
	
	// Internal function to create transparent material counterparts for fading
	void CreateTransparentMaterial(Material[] _sourceMaterials, Hashtable _ht) {
		foreach (Material _mat in _sourceMaterials) {
			_ht.Add(_mat, new Material(Shader.Find("SpaceUnity/Asteroid Transparent")));
			((Material) _ht[_mat]).SetTexture("_MainTex", _mat.GetTexture("_MainTex"));		
			((Material) _ht[_mat]).color = _mat.color;
		}
	}
	
	// Internal function to allow weighted random selection of materials
	static T WeightedRandom<T>(SortedList<int, T> _list) {
		int _max = _list.Keys[_list.Keys.Count-1];
		int _random = Random.Range(0, _max);
		foreach (int _key in _list.Keys) {
			if (_random <= _key) {
				return _list[_key];
			}
		}
   		return default(T);
	}
}

Commits for Nextrek/3DSpace/Assets/SpaceUnity/Scripts/SU_AsteroidField.cs

Diff revisions: vs.
Revision Author Commited Message
168 Diff Diff LMancini picture LMancini Wed 08 Apr, 2015 12:33:35 +0000
112 Diff Diff FMMortaroli picture FMMortaroli Thu 09 Oct, 2014 14:21:59 +0000
107 FMMortaroli picture FMMortaroli Thu 09 Oct, 2014 11:56:46 +0000