Skip to content

Commit 3760b58

Browse files
committed
##2016-11-07 V0.3
- Done: Attached transform handlers & lable Modular system script(Now it can be generated when scene started). Weight based randomizer. Random seed mode and starting method are working properly.
1 parent 104526f commit 3760b58

6 files changed

Lines changed: 249 additions & 17 deletions

File tree

Example/Example.unity

1.93 KB
Binary file not shown.
124 Bytes
Binary file not shown.

ModularSystem/Editor/ModularEditor.cs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public class ModularEditor : Editor{
2323

2424
private int drawingListIndex;
2525
private int partListIndex;
26-
//private ReorderableList activateList;
26+
private ReorderableList activateList;
2727
private GameObject lastPreviewGameObject;
2828
private Part selectingPart;
2929
private List<GameObject> previewGameObjects = new List<GameObject>();
@@ -52,6 +52,29 @@ protected void OnDisable()
5252

5353
}
5454

55+
void OnSceneGUI( )
56+
{
57+
// draw handles
58+
59+
foreach(PartSet ps in modularSystem.partSetList.ToArray()){
60+
61+
Handles.color = Color.blue;
62+
GUIStyle style = new GUIStyle();
63+
style.normal.textColor = Color.red;
64+
65+
Handles.Label(ps.attachTransform.position, ps.name + "[" + ps.partList.Count + "]", style);
66+
Handles.DrawWireCube(ps.attachTransform.position,new Vector3(0.2f,0.2f,0.2f));
67+
68+
if(activateList!= null && activateList.list == ps.partList){
69+
70+
ps.attachTransform.position = Handles.PositionHandle(ps.attachTransform.position,Quaternion.identity);
71+
}
72+
73+
74+
75+
}
76+
}
77+
5578

5679

5780
public override void OnInspectorGUI()
@@ -126,7 +149,7 @@ public override void OnInspectorGUI()
126149

127150
case StartingMethod.OnCall:
128151

129-
EditorGUILayout.HelpBox("Procedural generation will be execute when player called [ModuleSystem.StartGenerate] function.",MessageType.Info);
152+
EditorGUILayout.HelpBox("Procedural generation will be executed when player called [ModuleSystem.StartGenerate] function.",MessageType.Info);
130153

131154
break;
132155

@@ -176,7 +199,7 @@ public override void OnInspectorGUI()
176199

177200
if(ps.attachTransform && ps.attachTransform.gameObject){
178201

179-
if(ps.name.Contains("new Part Set")) ps.name = ps.attachTransform.gameObject.name;
202+
if(ps.name.Contains("new Part Set") || ps.name == "") ps.name = ps.attachTransform.gameObject.name;
180203

181204
}
182205

@@ -259,6 +282,11 @@ public override void OnInspectorGUI()
259282

260283
Repaint();
261284

285+
if(GUI.changed)
286+
{
287+
288+
EditorUtility.SetDirty(target);
289+
}
262290
}
263291

264292

@@ -349,7 +377,7 @@ public void CreateNewPart(ReorderableList rl){
349377
newPart.rotation = Vector3.zero;
350378
newPart.scale = Vector3.one;
351379

352-
newPart.weight = 0;
380+
newPart.weight = 1;
353381

354382
rl.list.Add(newPart);
355383

@@ -368,7 +396,7 @@ public void SelectPartElement(ReorderableList list){
368396
ClearPreviewGameObjects();
369397

370398
int index = list.index;
371-
//activateList = list;
399+
activateList = list;
372400
selectingPart = (Part)list.list[index];
373401
Transform attachTransform = modularSystem.partSetList[drawingListIndex].attachTransform;
374402

@@ -425,7 +453,7 @@ public void DrawPartElement(Rect rect, int index, bool isActive, bool isFocused)
425453

426454
p.prefab = (GameObject)EditorGUI.ObjectField(new Rect(rect.x + 10, rect.y + 110, rect.width - 18, 15),"Part Prefab:",p.prefab,typeof(GameObject),true);
427455

428-
if(p.prefab && p.name.Contains("NewPart")) p.name = p.prefab.name;
456+
if(p.prefab && p.name.Contains("NewPart") || p.name == "") p.name = p.prefab.name;
429457
}
430458

431459

ModularSystem/ModularClass.cs

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,115 @@ public class Part{
4141

4242
}
4343

44-
44+
4545
public enum StartingMethod{
4646

4747
Awake, OnCall
4848
}
4949

50-
50+
5151
public enum RandomSeedMode{
5252

5353
Default, Manual, PositionBased
5454
}
55+
56+
57+
58+
59+
/// <summary>
60+
/// Static class to improve readability
61+
/// Example:
62+
/// <code>
63+
/// var selected = WeightedRandomizer.From(weights).TakeOne();
64+
/// </code>
65+
///
66+
/// </summary>
67+
68+
[System.Serializable]
69+
public static class WeightedRandomizer
70+
{
71+
public static WeightedRandomizer<R> From<R>(Dictionary<R, int> spawnRate)
72+
{
73+
return new WeightedRandomizer<R>(spawnRate);
74+
}
75+
}
76+
77+
[System.Serializable]
78+
public class WeightedRandomizer<T>
79+
{
80+
//public System.Random _random = new System.Random();
81+
private Dictionary<T, int> _weights;
82+
83+
/// <summary>
84+
/// Instead of calling this constructor directly,
85+
/// consider calling a static method on the WeightedRandomizer (non-generic) class
86+
/// for a more readable method call, i.e.:
87+
///
88+
/// <code>
89+
/// var selected = WeightedRandomizer.From(weights).TakeOne();
90+
/// </code>
91+
///
92+
/// </summary>
93+
/// <param name="weights"></param>
94+
public WeightedRandomizer(Dictionary<T, int> weights)
95+
{
96+
_weights = weights;
97+
}
98+
99+
/// <summary>
100+
/// Randomizes one item
101+
/// </summary>
102+
/// <param name="spawnRate">An ordered list withe the current spawn rates. The list will be updated so that selected items will have a smaller chance of being repeated.</param>
103+
/// <returns>The randomized item.</returns>
104+
public T TakeOne()
105+
{
106+
// Sorts the spawn rate list
107+
var sortedSpawnRate = Sort(_weights);
108+
109+
// Sums all spawn rates
110+
int sum = 0;
111+
foreach (var spawn in _weights)
112+
{
113+
sum += spawn.Value;
114+
}
115+
116+
// Randomizes a number from Zero to Sum
117+
//int roll = _random.Next(0, sum);
118+
int roll = Random.Range(0, sum);
119+
120+
// Finds chosen item based on spawn rate
121+
T selected = sortedSpawnRate[sortedSpawnRate.Count - 1].Key;
122+
foreach (var spawn in sortedSpawnRate)
123+
{
124+
if (roll < spawn.Value)
125+
{
126+
selected = spawn.Key;
127+
break;
128+
}
129+
roll -= spawn.Value;
130+
}
131+
132+
// Returns the selected item
133+
return selected;
134+
}
135+
136+
137+
private List<KeyValuePair<T, int>> Sort(Dictionary<T, int> weights)
138+
{
139+
var list = new List<KeyValuePair<T, int>>(weights);
140+
141+
// Sorts the Spawn Rate List for randomization later
142+
list.Sort(
143+
delegate(KeyValuePair<T, int> firstPair,
144+
KeyValuePair<T, int> nextPair)
145+
{
146+
return firstPair.Value.CompareTo(nextPair.Value);
147+
}
148+
);
149+
150+
return list;
151+
}
152+
}
55153

56154

57155
}

ModularSystem/ModularSystem.cs

Lines changed: 92 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,35 +11,121 @@
1111

1212
namespace ModularSystem{
1313

14-
[ExecuteInEditMode]
14+
1515
public class ModularSystem : MonoBehaviour {
1616

1717
// the random.seed value as int
1818
public int randomSeed;
1919

2020
// the seed generate mode
21-
public RandomSeedMode mySeedMode = RandomSeedMode.Default;
21+
public RandomSeedMode mySeedMode;
2222

2323
// the part generation mode
24-
public StartingMethod myStartMethod = StartingMethod.Awake;
25-
24+
public StartingMethod myStartMethod;
2625

2726
public List<PartSet> partSetList = new List<PartSet>();
2827

28+
[SerializeField]
29+
private List<Dictionary<Part, int>> weightedPartSetList = new List<Dictionary<Part, int>>();
30+
2931
// Awake is called when the script instance is being loaded.
3032
protected void Awake()
3133
{
3234

35+
if(weightedPartSetList.Count > 0) weightedPartSetList.Clear();
36+
37+
// Initialize the weighted random list
38+
39+
foreach(PartSet ps in partSetList.ToArray()){
40+
41+
42+
43+
foreach (Part p in ps.partList.ToArray())
44+
{
45+
46+
int index = partSetList.IndexOf(ps);
47+
48+
//if(p.weight == 0) p.weight = 1;
49+
50+
weightedPartSetList.Add(new Dictionary<Part,int>());
51+
weightedPartSetList[index].Add(p, p.weight);
52+
53+
}
54+
55+
}
56+
57+
// Initialize the random seed
58+
59+
60+
switch(mySeedMode){
61+
62+
case RandomSeedMode.Default:
63+
64+
// do nothing when it's on default mode
65+
66+
break;
67+
68+
case RandomSeedMode.Manual:
69+
70+
// use assigned random seed
71+
72+
Random.InitState(randomSeed);
73+
74+
75+
76+
break;
77+
78+
case RandomSeedMode.PositionBased:
79+
80+
// use position to generate seed
81+
82+
int _randomSeed = transform.position.GetHashCode();
83+
84+
Random.InitState(_randomSeed);
85+
86+
break;
87+
88+
}
89+
3390
}
3491

3592
// Use this for initialization
3693
void Start () {
94+
95+
96+
if(myStartMethod == StartingMethod.Awake){
97+
98+
// generate on Start
99+
100+
StartGenerate();
101+
102+
}
37103

38104
}
39105

40-
// Update is called once per frame
41-
void Update () {
106+
107+
108+
public void StartGenerate(){
109+
110+
// Start generating gameobejcts
42111

112+
foreach(PartSet _ps in partSetList.ToArray()){
113+
114+
if(!_ps.isActivate) return;
115+
116+
117+
int index = partSetList.IndexOf(_ps);
118+
Part _part = WeightedRandomizer.From(weightedPartSetList[index]).TakeOne();
119+
120+
GameObject _gameobject = Instantiate(_part.prefab,_ps.attachTransform.position, Quaternion.Euler(_ps.attachTransform.localEulerAngles)) as GameObject;
121+
122+
_gameobject.transform.SetParent(_ps.attachTransform,true);
123+
124+
_gameobject.transform.position += _part.position;
125+
_gameobject.transform.localEulerAngles = _part.rotation;
126+
_gameobject.transform.localScale = _part.scale;
127+
128+
}
43129

44130
}
45131
}

README.md

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
# Unity Modular System
22
Unity Modular system let's you create game object with procedural generated parts.
33

4+
With editable random seed mode and starting method, it provides the user unlimited possiablity.
5+
6+
No coding skill required.
7+
8+
# How to use it?
9+
10+
1. First, attach the modular system script to the gameobject (as root) you want to have random procedural generated parts.
11+
2. Create some empty gameobjects under the root gameobject you just created, those are the basic position your procedural generated parts will based on.
12+
3. In the inspector, under the modular system script, create some partsets and drag those empty gameobjects to the "attachTransform".
13+
4. Add prefabs for each part sets.
14+
5. Adjust them individually.
15+
6. You can press "Preview" to preview the final product.
16+
7. Click on individual part will let you preview the part, drag will adjust the postion of attach transform. You can modify the position, rotation and scale for each parts individually.
17+
418
# Screenshots
519

620
- Editor View
@@ -13,16 +27,22 @@ Unity Modular system let's you create game object with procedural generated part
1327

1428
# Update Logs
1529

16-
2016-11-06 V0.2
30+
##2016-11-07 V0.3
31+
- Done: Attached transform handlers & lable
32+
Modular system script(Now it can be generated when scene started).
33+
Weight based randomizer.
34+
Random seed mode and starting method are working properly.
35+
36+
##2016-11-06 V0.2
1737
- Done: Generate preview in editor(Can be previewed with either singel or all part(s))
1838
Example scene using free asset from asset store.
1939

20-
2016-11-05 V0.1b
40+
##2016-11-05 V0.1b
2141
- Done: Name is now based on gameobjct's name if user hasn't define it.
2242
Random Seed manually generate.
2343
Starting method configuration.
2444

25-
2016-11-05 V0.1a
45+
##2016-11-05 V0.1a
2646

2747
- Done: Basic classes defined.
2848
Editor Window complete.

0 commit comments

Comments
 (0)