Table of Contents
Crowd System:
First Design:
The hordes system went through many different iterations.
The first concept was inspired by Dream Luigi of Mario & Luigi: Dream Team Bros. and by a Clank minigame of the Ratchet and Clank saga.
It was fairly simple, for each 10 new members a new form would be unlocked, as depicted in this diagram:
Maybe with more development time I would have made the Giant Chicken Form the way I wanted but in the end I'm happy with what I've achieved.
Final Design:
The final version of the Controller is based solely on the Leader-Follower Mode. It works through a Singleton, the HiveMind that is being controlled by the HiveController. The HiveMind then relays all its inputs to the available leaders and they do the same with their followers.
A HiveMember can change Behaviour at any time with any other Behaviour using common methods.
1 class_name HiveBehaviour
2 extends Node2D
3
4 var is_active := false
5 var member : HiveMember
6
7 func start(_info := {}): pass
8
9 func update_orders(_direction : int, _wants_jump : bool, _wants_tower : bool) -> void: pass
10
11 func end(): queue_free()
12
13
14 func become_follower(_info := {}): pass
15
16 func become_leader(_info := {}): pass
17
18 func become_inactive(_info := {}): pass
19
20
21 func free_member(): pass
Probably this isn't the most effective way to do this but it works and solves many problems I had with other iterations of such system.
To describe the modes in which a Member can change Behaviour, I synthesized all possible cases in 4 events described by this diagram. This helped me tackle the code and implementation of such system.
Case Study: LeaderHiveBehaviour
The following section is a very long chunk of code, if you couldn't handle the first one don't go forwards. If you are into this, go for it, it's far from perfect but it may be interesting to look at idunno.
1 class_name LeaderHiveBehaviour
2 extends HiveBehaviour
3
4 @export var leader_boost_factor := 1.0
5
6 var followers : Array[HiveMember] = []
7
8 func start(info := {}):
9 # Reset member's movement variables
10
11 member.leader_boost = leader_boost_factor
12 if info.has("followers"):
13 followers = info["followers"]
14 for follower in followers:
15 follower.behaviour.become_follower({"leader_member": member})
16
17 func end():
18 member.leader_boost = 1
19 member.z_index -= 1
20 queue_free()
21
22 func update_orders(new_direction : int, new_wants_jump : bool, new_wants_tower : bool) -> void:
23 if not is_active: return
24
25 # If hijacked order stop movement to followers
26
27 # If is able to tower and has requested:
28 # Try to build tower if not building one
29 # Stop building tower if already building
30
31 member.wants_jump = new_wants_jump and can_jump
32 member.direction = new_direction * (0.5 if building_tower else 1.0)
33
34 for follower in followers:
35 follower.behaviour.update_orders(new_direction, member.wants_jump, building_tower)
36
37 func _build_tower_started():
38 # Calling each follower by index to maintain order.
39
40 # This makes the followers all line up and start to do little jumps to get
41 # on top of each other.
42 for i in range(len(followers)):
43 if followers[i].behaviour is FollowerHiveBehaviour:
44 followers[i].behaviour._tower_jump()
45 await followers[i].behaviour.finished_jumping
46 if not building_tower: return
47
48 func become_follower(info := {}):
49 if info.has("leader_member"):
50 _release_followers(info["leader_member"])
51 member.change_behaviour(HiveMind.available_hive_behaviours[HiveMind.HiveBehaviours.FOLLOWER], {"leader_member": info["leader_member"]})
52 info["leader_member"].behaviour.followers.append(member)
53
54 func become_leader(_info := {}):
55 pass
56
57 func become_inactive(_info := {}):
58 for follower : HiveMember in followers:
59 follower.change_behaviour(HiveMind.available_hive_behaviours[HiveMind.HiveBehaviours.INACTIVE])
60 HiveMind.members_count -= 1
61 member.change_behaviour(HiveMind.available_hive_behaviours[HiveMind.HiveBehaviours.INACTIVE])
62
63 func free_member():
64 _release_followers()
65 HiveMind.members_count -= 1
66 is_active = false
67 member.queue_free()
68
69
70 func _release_followers(to_member : HiveMember = null):
71 HiveMind.leaders.erase(member)
72
73 # If to_member != null assign the followers to him
74 # otherwise pick the last follower and promote him to leader and assign the other followers to him
75
76 ## Activated when another Leader or an Inactive member gets near
77 func _on_control_box_area_entered(area : Area2D) -> void:
78 if is_active and not is_hijacked:
79 var hive_behaviour : HiveBehaviour = area.get_parent()
80
81 if hive_behaviour is HiveBehaviour:
82 hive_behaviour.become_follower({"leader_member": member})
83 _handle_new_follower()
84
85 ## Activated when the player gets too far
86 func _on_lost_check_box_body_exited(body) -> void:
87 if is_active and body is Player:
88 become_inactive()
89
90 func _handle_new_follower() -> void:
91 %CanFollowersJumpTimer.start()
92 can_jump = false
93 if len(followers) >= 10:
94 can_tower = true
95 else:
96 can_tower = false
97 building_tower = false