GameProgramming-Relationship-Graph

Keywords: Relationship graph, Random distribution, LineRenderer

Knuth-Durstenfeld Shuffle

Given an integer array nums, design an algorithm to randomly shuffle the array. All permutations of the array should be equally likely as a result of the shuffling.

KDShuffle-Process

1
2
3
4
5
6
7
8
9
vector<int> shuffle() {
int len = nums.size();
for(int i = len - 1; i >= 0; i--)
{
int j = rand() % (i + 1); //rand() % M : [0,M)
swap(nums[i],nums[j]);
}
return nums;
}

If we need to distribute the players at random positions, we can shuffle the position array and assign them to the players in sequence.

Ellipse

Though we know the formula of ellipse $\frac{x^2}{a^2} + \frac{y^2}{b^2} = 1$, it’s not efficient to compute the data strictly by it. Sometimes we view an ellipse as a squashed circle, which means $x$ stay the same, but $y$ compress by a factor.

ellipse

For example, there are several elliptical concentric tracks, we want to sample $N$ points randomly along each orbitals (for $N$ players).

Method: Set a maxValue $M$, denotes the maximum number of points can be sampled from one orbital, $N \leq M$.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
for(int i = 1; i <= orbitalNums; i++)
{
int maxPointNum = M; //M can be set by designer

//uniformly sample M points in the orbital
for(int j = 0; j < maxPointNum; j++)
{
float x = Math.Cos(2pi / M * j);
float y = Math.Sin(2pi / M * j) * compressCoefficient; //compress y value

//the radius of the concentric circle is gradually increased
Vector2 point = Vector2(x,y) * circleRadius * i;

//add a perturbation(disturbance) avoiding rigid distribution
float disturbance = circleRadius * UnityEngine.Random.Range(-offset, offset);

orbit.Add(point.x + disturbance, point.y + disturbance);
}
//shuffle orbit array
shuffle(orbit);

//n players' positions <-- first n of the m points
assignPos();
}

sample M points

Connect Nodes Clockwisely

Sometimes we want to connect several nodes by specific rule.

For example, we want to connect those players on the first three orbits clockwisely, connect those players on the second four orbits clockwisely…In general, a closer orbit indicates a closer relationship to me.

Method:

  1. sort the players according to the degree of intimacy, decide which orbit and which position they belong to.
  2. group those players by groupId, hash: <groupId, List<accountId>()>
  3. create a LinkedList clockwisely, linklist records the previous and next neighbor accountId for this node(player)
  4. Render the line while iterate linklist, thus we can see the palyers distribute on the orbits and connected clockwisely by group.

clockwise

1
2
3
4
5
6
7
8
//Vector B should be in the clickwies direction of A
float a = Vector2.Angle(Vector2.up, A);
float b = Vector2.Angle(Vector2.up, B);
a = A.x >= 0 ? a : 360 - a;
b = B.x >= 0 ? b : 360 - b;
//B is in the clockwise direction of A
if(b > a){A->next = B}
else{B->next = A}

Also, the line effect is realized by LineRenderer, inner class in Unity.

Play Animations In Order

Sometimes animation can be broken down into successive steps, for example, 1. the two lines disappear 2. two nodes move toward each other 3. new lines generate. Step1,2,3 must be done in sequence, but in animation1, the two lines disappear animation can run at the same time.

The normal code can write like this:

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
void updatedata()
{
//data change causes the animation to occur
StartCoroutine(PlayXAnimation);
}
IEnumerator PlayXAnimation()
{
//Step1.
yield return PlayStep1Animation();
//Step2.
yield return PlayStep2Animation();
//Step3.
yield return PlayStep3Animation();
}
//Step1.
IEnumerator PlayStep1Animation()
{
StartCoroutine(Play1.1);
StartCoroutine(Play1.2);
//suppose 3f is the animation length
yield return new WaitForSeconds(3f);
}
//Step2.
IEnumerator PlayStep2Animation()
{
...
yield return new WaitForSeconds(3f);
}
//Step3.
IEnumerator PlayStep3Animation()
{
...
yield return new WaitForSeconds(3f);
}
//Step1.1
IEnumerator Play1.1()
{
AnimationClip clip = getclip("clipname");
if(clip)
{
//Animation ani;
ani.play("clipname");
}
}

The essence of Coroutine is an Iterator.

Click and LongPress

Sometimes we need to distinguish the click action and longpress action on one object. We can think of it in terms of state machines.

For example, once we press our fingure on this object, $t \leq 0.2s$, it’s click action, $0.2s < t \leq 0.7s$, it’s longpress action. once the press time $t > 0.7s$, we think the action is invalid, it’s reset to idle state.

press

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
enum State
{
Idle = 0,
Click = 1,
Holding = 2,
LongHolding = 3
}
State st = State.Idle; //no click and press

float t = 0f;
bool pressed = false;

//UIEventTrigger
void init()
{
EventDelegate onPress = new EventDelegate(onPressobBtn);
EventDelegate onRelease = new EventDelegate(onReleaseobBtn);
UIEventTrigger trigger = ob.gameObject.GetComponent<UIEventTrigger>();
if(trigger != null)
{
trigger.onPress.Add(onPress);
trigger.onRelease.Add(onRelease);
}
}
void onPressobBtn()
{
pressed = true;
}
void onReleaseobBtn()
{
pressed = false;
}
//update function
void Update()
{
switch (st)
{
case State.Idle:
if(pressed)
{
st = State.Click;
t = 0;
}
break;
case State.Click:
if(!pressed)
{
st = State.Idle;
//click effect: show click UI, cause click effects are often displayed at the moment of release
return;
}
t += Time.deltaTime;
if(t > 0.2)
{
st = State.Holding;
//holding effect: show holding UI
}
break;
case State.Holding:
if(!pressed)
{
st = State.Idle;
}
//holding effect: cause holding effects are often displayed with holding time. like fillAmount

if(t > 0.7)
{
st = State.LongHolding;
//hide holding UI
}
break;
case State.LongHolding:
if(!pressed)
{
st = State.Idle;
}
break;
}
}

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×