A Stellar Adventure

Mini side-project

4 min. read

About

The gameplay was originally designed to feel “sticky,” but it was iterated later to feel more “floaty” and “jumpy” since people liked that more after doing blind playtests.

The goal of this project was to learn something new in Unity. It is a prototype that I spent 20 hours working on, but if people like it, I was thinking of designing a few levels for people to play.

Disclaimer

Credit goes to Deviant Art user Air-City for the character design. I originally used their design without permission for a school project, and I do not plan on using this asset any further.

Player Controller System

I have seperated the Cerri Character class and Player Controller class so that I can have a system to handle input recording. I plan to prototype a system that records the gameplay so that the player can race against a “ghost.”

Cerri Character Classes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class CH_PlayerController : CH_Cerri
{
/// <summary>
/// </summary>
private void Start()
{
Initialize();
}

/// <summary>
/// </summary>
private void Update()
{
HorizontalInput = Input.GetAxis("Horizontal");
VerticalInput = Input.GetAxis("Vertical");
JumpInput = CharacterState == CharacterState.Grounded ? Input.GetButton("Jump") : Input.GetButtonDown("Jump");
DashInput = CharacterState == CharacterState.Grounded ? Input.GetButton("Dash") : Input.GetButtonDown("Dash");
AttackInput = Input.GetButtonDown("Attack");
AttackReleased = Input.GetButtonUp("Attack");
PlayerUpdate();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// This class will handle the main character.
public class CH_Cerri : CH_Base
{
/// <summary>
/// </summary>
private void Update() => PlayerUpdate();

/// Updates all the Character Mechanics
internal void PlayerUpdate()
{
ResetInputCache();
HandleCooldowns();
HandleMoveInput();
HandleWallSlide();
HandleJumpInput();
HandleDashInput();
HandleDashActive();
HandleAttackInput();
HandleAttackRelease();
}
}

Ability System

Here’s a pretty cool system that I’ve set up. This handles all the cooldowns of the actives/abilities that the player uses.

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
public class AbilityObject
{
public string Name { get; }
public float CDDuration { get; }
public float CDCurrent { get; private set; }
public bool Available => CDCurrent == 0;
public event EventHandler OnCDFinished;

/// Constructor
public AbilityObject(string name, float duration)
{
Name = name;
CDDuration = duration;
}

/// Tries to use the current ability.
public bool Use()
{
if (CDCurrent > 0)
return false;

CDCurrent = CDDuration;
return true;
}

/// Updates the current ability cooldown.
public void UpdateCD(float dt)
{
if (CDCurrent <= 0)
return;

CDCurrent = Mathf.Clamp(CDCurrent - dt, 0, int.MaxValue);

if (CDCurrent <= 0)
{
CDCurrent = 0;
OnCDFinished?.Invoke(this, new EventArgs());
}
}

/// Resets the Ability's Cooldown
public void ResetCD()
{
if (CDCurrent <= 0)
return;

CDCurrent = 0;
OnCDFinished?.Invoke(this, new EventArgs());
}
}

// ...
// Example usage

public class CH_Cerri : CH_Base
{
/// List of Player's abilities. Used to track Cooldowns and stuff.
private Dictionary<PlayerAbility, AbilityObject> _abilities;

/// List of Player's Active abilities. Used to track Cooldowns and stuff.
private Dictionary<PlayerActives, AbilityObject> _actives;

/// <summary>
/// </summary>
private void Start(){
_abilities = new Dictionary<PlayerAbility, AbilityObject>()
{
{PlayerAbility.Jump, new AbilityObject("Jump", REACTION_INPUT_TOLERANCE)},
{PlayerAbility.Dash, new AbilityObject("Dash", _dashCooldown)},
{PlayerAbility.Attack, new AbilityObject("Attack", _attackCooldown)},
{PlayerAbility.FastFall, new AbilityObject("FastFall", 1000)},
{PlayerAbility.WallSlide, new AbilityObject("WallSlide", REACTION_INPUT_TOLERANCE)}
};

_actives = new Dictionary<PlayerActives, AbilityObject>()
{
{PlayerActives.Dashing, new AbilityObject("Dashing", _dashDuration)},
{PlayerActives.WallJumping, new AbilityObject("WallJumping", _wallJumpDuration)},
{PlayerActives.WallJumpIgnoreReverse, new AbilityObject("WallJumpIgnoreReverse", _reverseInputDelay)}
};
}
/// <summary>
/// </summary>
private void Update() => HandleCooldowns;

/// Updates every Ability and Actives.
private void HandleCooldowns()
{
foreach (var ab in _abilities)
ab.Value.UpdateCD(Time.deltaTime);

foreach (var ac in _actives)
ac.Value.UpdateCD(Time.deltaTime);
}
}