Skip to content

Commit c9f1972

Browse files
committed
LINQExtensions and RandomBag
1 parent 4b46f22 commit c9f1972

File tree

7 files changed

+565
-3
lines changed

7 files changed

+565
-3
lines changed

Examples/LINQExtensionsExamples.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using UnityEngine;
5+
6+
namespace UnityUtilities.Examples
7+
{
8+
public class LINQExtensionsExamples : MonoBehaviour
9+
{
10+
[SerializeField] Transform[] elements;
11+
12+
void Awake()
13+
{
14+
int[] items = new int[] {1, 2, 3, 4, 5};
15+
16+
// Gets a random item
17+
Debug.Log(items.RandomElement());
18+
19+
// Shuffles the array in place
20+
items.Shuffle();
21+
22+
// Outputs the array on one line. Great for debugging.
23+
// Example output:
24+
// "3", "5", "2", "1", "4"
25+
Debug.Log(items.ToOneLineString());
26+
}
27+
28+
void Update()
29+
{
30+
if (Input.GetMouseButtonDown(0))
31+
{
32+
// Get the x/y position of the click (for an orthographic camera)
33+
var mousePositionWorld = Camera.main.ScreenToWorldPoint(Input.mousePosition);
34+
35+
// Gets the element closest to the mouse click
36+
var nearestElement = elements.Nearest(mousePositionWorld);
37+
38+
Debug.Log("Nearest element " + nearestElement.name + " at position " + nearestElement.position);
39+
}
40+
}
41+
}
42+
}

Examples/RandomBagExample.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using System;
2+
using System.Text;
3+
using UnityEngine;
4+
5+
namespace UnityUtilities.Examples
6+
{
7+
public class RandomBagExample : MonoBehaviour
8+
{
9+
private enum TetrisPiece
10+
{
11+
Line, Square, T, J, L, S, Z
12+
}
13+
14+
RandomBag<TetrisPiece> pieceBag;
15+
16+
void Awake()
17+
{
18+
// Get an array with each value of TetrisPiece
19+
TetrisPiece[] tetrisPieceArray = (TetrisPiece[]) Enum.GetValues(typeof (TetrisPiece));
20+
21+
// Create the bag containing two instances of every value of TetrisPiece
22+
pieceBag = new RandomBag<TetrisPiece>(tetrisPieceArray, 2);
23+
24+
// Gets 50 items from the bag. The bag will be filled with 14 TetrisPieces and
25+
// automatically refilled with 14 more when needed. No two pieces will ever be
26+
// more than 14 calls apart - and even that will only happen if that piece was
27+
// the first and last item in the current 14 piece bag filling.
28+
StringBuilder str = new StringBuilder();
29+
for (var i = 0; i < 50; i++)
30+
{
31+
str.Append(pieceBag.PopRandomItem());
32+
str.Append(", ");
33+
}
34+
35+
Debug.Log(str);
36+
37+
// Example output:
38+
// T, Z, J, Square, Z, S, L, L, S, Line, J, Line, Square, T, Square, Z,
39+
// S, T, Line, Square, Z, T, J, Line, S, L, J, L, S, Z, Line, J, Line,
40+
// J, L, L, S, T, T, Z, Square, Square, Z, T, S, Z, J, J, L, Line,
41+
}
42+
}
43+
}

LINQExtensions/LINQExtensions.cs

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using UnityEngine;
5+
6+
namespace UnityUtilities
7+
{
8+
/// <summary>
9+
/// A collection of extension methods for <see cref="IEnumerable{T}"/>, <see cref="List{T}"/> and arrays.
10+
/// </summary>
11+
public static class LINQExtensions
12+
{
13+
/// <summary>
14+
/// Takes a collection, generates values from the items and and returns the item with the lowest generated value.
15+
/// Useful for example to find the closest item. Reverse the generated values to find the item with the highest generated value.
16+
///
17+
/// This returns the same as list.Where(element => predicateValue(valueConverter(element))).OrderBy(valueConverter).First(), but it
18+
/// a) doesn't need to order the whole list and
19+
/// b) doesn't need to call valueConverted more than once per element.
20+
/// </summary>
21+
/// <typeparam name="TElement">The collection element type.</typeparam>
22+
/// <typeparam name="TValue">The generated value type.</typeparam>
23+
/// <param name="list">The list of elements.</param>
24+
/// <param name="valueConverter">The method to convert an element to a generated value used for ordering.</param>
25+
/// <param name="predicateValue">A predicate testing whether the generated value is permitted. If true, the element is used; if false, the element is skipped.</param>
26+
/// <returns>The first element by the generated value in order that succeeded the predicateValue test.</returns>
27+
public static TElement FirstByGeneratedValue<TElement, TValue>(this IEnumerable<TElement> list, Func<TElement, TValue> valueConverter, Predicate<TValue> predicateValue = null)
28+
where TValue : IComparable
29+
{
30+
var isFirstElement = true;
31+
TElement bestElement = default(TElement);
32+
TValue bestValue = default(TValue);
33+
34+
// For each element...
35+
foreach (var element in list)
36+
{
37+
// Generate its value
38+
var value = valueConverter(element);
39+
40+
// Check whether its value is permitted
41+
if ((predicateValue != null) && (!predicateValue(value)))
42+
continue;
43+
44+
// If it's the first permitted element or better than the previous best element...
45+
if (isFirstElement || (value.CompareTo(bestValue) < 0))
46+
{
47+
// ...set it as the best element
48+
isFirstElement = false;
49+
bestElement = element;
50+
bestValue = value;
51+
}
52+
}
53+
54+
return bestElement;
55+
}
56+
57+
/// <summary>
58+
/// Returns the element nearest to the referencePoint and in minimum/maximum range.
59+
/// </summary>
60+
/// <typeparam name="TElement">The collection element type. Needs to be a subclass of <see cref="Component"/>.</typeparam>
61+
/// <param name="list">The list of elements to get the closest.</param>
62+
/// <param name="referencePoint">A reference point to get the distance from.</param>
63+
/// <param name="minDistance">Optional: The minimum distance.</param>
64+
/// <param name="maxDistance">Optional: The maximum distance.</param>
65+
/// <returns>The element nearest to the referencePoint and in minimum/maximum range.</returns>
66+
public static TElement Nearest<TElement>(this IEnumerable<TElement> list, Vector3 referencePoint, float minDistance = 0, float maxDistance = float.PositiveInfinity)
67+
where TElement : Component
68+
{
69+
// Create the predicate value from the min/max distance.
70+
Predicate<float> predicateValue = null;
71+
if ((minDistance != 0) || !float.IsPositiveInfinity(maxDistance))
72+
{
73+
var minDistanceSq = minDistance * minDistance;
74+
var maxDistanceSq = maxDistance * maxDistance;
75+
predicateValue = (distanceSq => (distanceSq >= minDistanceSq) && (distanceSq <= maxDistanceSq));
76+
}
77+
78+
// Return the nearest element to the reference point in the given range.
79+
return list.FirstByGeneratedValue(component => (component.transform.position - referencePoint).sqrMagnitude, predicateValue);
80+
}
81+
82+
/// <summary>
83+
/// Shuffles an array in place.
84+
/// </summary>
85+
/// <typeparam name="T">The array element type.</typeparam>
86+
/// <param name="list">The array to shuffle.</param>
87+
public static void Shuffle<T>(this T[] list)
88+
{
89+
var count = list.Length;
90+
for (int i1 = 0; i1 < count; i1++)
91+
{
92+
var i2 = UnityEngine.Random.Range(0, count);
93+
var element = list[i1];
94+
list[i1] = list[i2];
95+
list[i2] = element;
96+
}
97+
}
98+
99+
/// <summary>
100+
/// Shuffles an list in place.
101+
/// </summary>
102+
/// <typeparam name="T">The list element type.</typeparam>
103+
/// <param name="list">The list to shuffle.</param>
104+
public static void Shuffle<T>(this List<T> list)
105+
{
106+
var count = list.Count;
107+
for (int i1 = 0; i1 < count; i1++)
108+
{
109+
var i2 = UnityEngine.Random.Range(0, count);
110+
var element = list[i1];
111+
list[i1] = list[i2];
112+
list[i2] = element;
113+
}
114+
}
115+
116+
/// <summary>
117+
/// Returns a random element from the array.
118+
/// </summary>
119+
/// <typeparam name="T">The array element type.</typeparam>
120+
/// <param name="array">The array to return an element from.</param>
121+
/// <returns>A random element from the array.</returns>
122+
public static T RandomElement<T>(this T[] array)
123+
{
124+
var index = UnityEngine.Random.Range(0, array.Length);
125+
return array[index];
126+
}
127+
128+
/// <summary>
129+
/// Returns a random element from the list.
130+
/// </summary>
131+
/// <typeparam name="T">The list element type.</typeparam>
132+
/// <param name="array">The list to return an element from.</param>
133+
/// <returns>A random element from the list.</returns>
134+
public static T RandomElement<T>(this List<T> list)
135+
{
136+
var index = UnityEngine.Random.Range(0, list.Count);
137+
return list[index];
138+
}
139+
140+
/// <summary>
141+
/// Calls ToString() on every element of the list, puts [encapsulate] directly before and after the result
142+
/// and then concatenates the results with [seperator] between them.
143+
/// </summary>
144+
/// <typeparam name="T">The collection element type.</typeparam>
145+
/// <param name="list">The collection to concatenate.</param>
146+
/// <param name="separator">The seperator between entries.</param>
147+
/// <param name="encapsulate">The string to put directly before and after every item.</param>
148+
/// <returns>A string containing the ToString() results of all items.</returns>
149+
public static string ToOneLineString<T>(this IEnumerable<T> list, string separator = ", ", string encapsulate = "\"")
150+
{
151+
var useEncapsulate = encapsulate.Length > 0;
152+
153+
var result = new StringBuilder();
154+
foreach (var element in list)
155+
{
156+
if (result.Length > 0)
157+
result.Append(separator);
158+
159+
if (useEncapsulate)
160+
result.Append(encapsulate);
161+
162+
result.Append(element);
163+
164+
if (useEncapsulate)
165+
result.Append(encapsulate);
166+
}
167+
168+
return result.ToString();
169+
}
170+
}
171+
}

LINQExtensions/README.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# LINQExtensions
2+
3+
A collection of extension methods for `IEnumerable`s, `List`s and arrays.
4+
5+
## Examples
6+
7+
# RandomElement/Shuffle/ToOneLineString
8+
9+
```C#
10+
int[] items = new int[] {1, 2, 3, 4, 5};
11+
12+
// Gets a random item
13+
Debug.Log(items.RandomElement());
14+
15+
// Shuffles the array in place
16+
items.Shuffle();
17+
18+
// Outputs the array on one line. Great for debugging.
19+
// Example output:
20+
// "3", "5", "2", "1", "4"
21+
Debug.Log(items.ToOneLineString());
22+
```
23+
24+
# Nearest
25+
26+
```C#
27+
public class LINQExtensionsExamples : MonoBehaviour
28+
{
29+
[SerializeField] Transform[] elements;
30+
31+
void Update()
32+
{
33+
if (Input.GetMouseButtonDown(0))
34+
{
35+
// Get the x/y position of the click (for an orthographic camera)
36+
var mousePositionWorld = Camera.main.ScreenToWorldPoint(Input.mousePosition);
37+
38+
// Gets the element closest to the mouse click
39+
var nearestElement = elements.Nearest(mousePositionWorld);
40+
41+
Debug.Log("Nearest element " + nearestElement.name + " at position " + nearestElement.position);
42+
}
43+
}
44+
}
45+
```
46+
47+
## Dependencies
48+
49+
None.

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ If you find any bugs or have suggestions, please add an [Issue](https://github.c
88

99
## Overview
1010
* [Countdown](https://github.com/TobiasWehrum/unity-utilities/tree/master/Countdown): Useful for things like cooldowns or spawn delays. It is also helpful for tweening things by using the `PercentElapsed` property.
11+
* [LINQExtensions](https://github.com/TobiasWehrum/unity-utilities/tree/master/LINQExtensions): A collection of extension methods for `IEnumerable`s, `List`s and arrays.
1112
* [NoiseOutputValue](https://github.com/TobiasWehrum/unity-utilities/tree/master/NoiseOutputValue): Enter a range and a speed in the editor, get an output value that fluctuates over time using [Perlin Noise](http://docs.unity3d.com/ScriptReference/Mathf.PerlinNoise.html).
12-
* [Range](https://github.com/TobiasWehrum/unity-utilities/tree/master/Range): Editable data types that takes an int/float range. Used for things like "Spawn 2 to 4 enemies."
13+
* [RandomBag](https://github.com/TobiasWehrum/unity-utilities/tree/master/RandomBag): A `RandomBag` gives you random items from a group while ensuring that in a certain interval every item was given back the same number of times.
14+
* [Range](https://github.com/TobiasWehrum/unity-utilities/tree/master/Range): Editable data types that take an `int`/`float` range. Used for things like "Spawn 2 to 4 enemies."
1315
* [Singleton](https://github.com/TobiasWehrum/unity-utilities/tree/master/Singleton): Allows easy and convenient creation of a Singleton. Optionally makes a Singleton persist between scenes while ensuring that only one exists.
1416

1517
## Usage
@@ -23,5 +25,6 @@ You can also just use selected scripts, but you should check the "Dependencies"
2325
The class documentation is available [here](http://tobiaswehrum.github.io/UnityUtilities/html/annotated.html).
2426

2527
## Changelog
26-
* 2015-05-09: Added the [class documentation website](http://tobiaswehrum.github.io/UnityUtilities/html/annotated.html).
27-
* 2015-05-08: Added [Countdown](https://github.com/TobiasWehrum/unity-utilities/tree/master/Countdown), [NoiseOutputValue](https://github.com/TobiasWehrum/unity-utilities/tree/master/NoiseOutputValue), [Range](https://github.com/TobiasWehrum/unity-utilities/tree/master/Range) and [Singleton](https://github.com/TobiasWehrum/unity-utilities/tree/master/Singleton).
28+
* 2016-05-15: Added [LINQExtensions](https://github.com/TobiasWehrum/unity-utilities/tree/master/LINQExtensions) and [RandomBag](https://github.com/TobiasWehrum/unity-utilities/tree/master/RandomBag).
29+
* 2016-05-09: Added the [class documentation website](http://tobiaswehrum.github.io/UnityUtilities/html/annotated.html).
30+
* 2016-05-08: Added [Countdown](https://github.com/TobiasWehrum/unity-utilities/tree/master/Countdown), [NoiseOutputValue](https://github.com/TobiasWehrum/unity-utilities/tree/master/NoiseOutputValue), [Range](https://github.com/TobiasWehrum/unity-utilities/tree/master/Range) and [Singleton](https://github.com/TobiasWehrum/unity-utilities/tree/master/Singleton).

RandomBag/README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# RandomBag
2+
3+
A RandomBag ensures that per interval [fillings * itemCount], every different item in it will be given back [fillings] times. Once the bag is empty, it is automatically refilled, either from a fixed array or by calling a delegate.
4+
5+
An example for that is used in [some implementations of Tetris](http://tetris.wikia.com/wiki/Random_Generator). The bag is filled with one instance (fillings=1) of each of the seven different pieces. Every time the next piece is needed, a random one is taken out of the bag until the bag is empty. That way, any two pieces are never longer than 14 pulls apart - and even that is only the case if the first and the last piece in the bag are the same one.
6+
7+
## Example
8+
9+
```C#
10+
public class RandomBagExample : MonoBehaviour
11+
{
12+
private enum TetrisPiece
13+
{
14+
Line, Square, T, J, L, S, Z
15+
}
16+
17+
RandomBag<TetrisPiece> pieceBag;
18+
19+
void Awake()
20+
{
21+
// Get an array with each value of TetrisPiece
22+
TetrisPiece[] tetrisPieceArray = (TetrisPiece[]) Enum.GetValues(typeof (TetrisPiece));
23+
24+
// Create the bag containing two instances of every value of TetrisPiece
25+
pieceBag = new RandomBag<TetrisPiece>(tetrisPieceArray, 2);
26+
27+
// Gets 50 items from the bag. The bag will be filled with 14 TetrisPieces and
28+
// automatically refilled with 14 more when needed. No two pieces will ever be
29+
// more than 14 calls apart - and even that will only happen if that piece was
30+
// the first and last item in the current 14 piece bag filling.
31+
StringBuilder str = new StringBuilder();
32+
for (var i = 0; i < 50; i++)
33+
{
34+
str.Append(pieceBag.PopRandomItem());
35+
str.Append(", ");
36+
}
37+
38+
Debug.Log(str);
39+
40+
// Example output:
41+
// T, Z, J, Square, Z, S, L, L, S, Line, J, Line, Square, T, Square, Z,
42+
// S, T, Line, Square, Z, T, J, Line, S, L, J, L, S, Z, Line, J, Line,
43+
// J, L, L, S, T, T, Z, Square, Square, Z, T, S, Z, J, J, L, Line,
44+
}
45+
}
46+
```
47+
48+
## Dependencies
49+
50+
* [LINQExtensions](https://github.com/TobiasWehrum/unity-utilities/tree/master/LINQExtensions)

0 commit comments

Comments
 (0)