Spirits of The Dreaming Tree

A 3D platformer with 2D gameplay based on the Brazilian Karajá tribe culture and style.
You fight your way through spirits that are threatening your village. Made in a 2.5-day game jam.

 

 

 

Time: ~30 hours
Team: Citra van Heeswijk (2D artist), Renée Vianen (2D/3D artist), Silvan Könings (2D artist), Jaron Kuijper (Programmer), Me (Programmer)
Engine: Unity 2017.1
Language(s): C#

 

Notes

We spent the majority of the first day planning.
This was my first time properly working together with another programmer on a game.

 

Scripts

Below you can look at some of the scripts I wrote for this project.
The conventions are kind of all over the place since this project is a collaborated quick game jam prototype.

A quick sum-up:

Player.cs
GlideState.cs

 

Player.cs

Used for managing stats and for integrating the state machine.

  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
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using SOTDT.Gameplay.Entity.State;
using SOTDT.Util;

namespace SOTDT.Gameplay.Entity
{
    public class Player : Damageable
    {
        #region Events

        private EventHandler m_GroundImpactEvent;
        private EventHandler m_RespawnEvent;

        public event EventHandler GroundImpactEvent { add { m_GroundImpactEvent += value; } remove { m_GroundImpactEvent -= value; } }
        public event EventHandler RespawnEvent { add { m_RespawnEvent += value; } remove { m_RespawnEvent -= value; } }

        #endregion Events

        #region Members

        private Rigidbody2D m_Rigidbody;
        private BoxCollider2D m_Collider;
        private Animator m_Animator;

        private Vector3 m_CurrentCheckpoint;
        private Vector3 m_OriginPos;

        private UIDeathText deathUI;

        #endregion Members

        #region Serialized Members

      [Header("Walking")] [SerializeField] private float m_WalkSpeed = 10.0f;
      [SerializeField] private int m_LastXDirection = 1;

      [Header("Jumping")] [SerializeField] private float m_AirborneSpeed = 5.0f;
      [SerializeField] private float m_JumpSpeed = 20.0f;
      [SerializeField] private float m_DoubleJumpSpeed = 30.0f;
      [SerializeField] private int m_MaxJumps = 2;
      [SerializeField] private int m_JumpsLeft;

      [Header("Gliding")] [SerializeField] private float m_GlideSpeed = 5.0f;
      [SerializeField] private float m_MaxGlideTime = 3.0f;

      [Header("Dive-kick")] [SerializeField] private float m_DiveHitRange = 1.0f;
      [SerializeField] private int m_DiveHitDamage = 2;
      [SerializeField] private Vector2 m_DiveVelocity;

      [Header("Punch Combo")] [SerializeField] private float m_MaxPunchComboTime = .3f;
      [SerializeField] private float m_PunchRange = 1.0f;
      [SerializeField] private int m_FirstPunchDamage = 1;
      [SerializeField] private int m_SecondPunchDamage = 2;
      [SerializeField] private int m_ThirdPunchDamage = 3;

      [Header("Misc.")] [SerializeField] private float m_GroundCheckRayLength = .05f;
      [SerializeField] private bool m_IsAirborne = false;
      [SerializeField] private IState m_State;
      [SerializeField] private bool m_PrintState = false;

        #endregion Serialized Members

        #region Properties

        public Rigidbody2D Rigidbody { get { return m_Rigidbody; } }
        public BoxCollider2D Collider { get { return m_Collider; } }
        public Animator Animator { get { return m_Animator; } }

        public float WalkSpeed { get { return m_WalkSpeed; } }
        public int LastXDirection { get { return m_LastXDirection; } set { m_LastXDirection = value; } }

        public float AirborneSpeed { get { return m_AirborneSpeed; } }
        public float JumpSpeed { get { return m_JumpSpeed; } }
        public float DoubleJumpSpeed { get { return m_DoubleJumpSpeed; } }
        public int MaxJumps { get { return m_MaxJumps; } }
        public int JumpsLeft { get { return m_JumpsLeft; } set { m_JumpsLeft = value; } }

        public float GlideSpeed { get { return m_GlideSpeed; } }
        public float MaxGlideTime { get { return m_MaxGlideTime; } }

        public float DiveHitRange { get { return m_DiveHitRange; } }
        public int DiveHitDamage { get { return m_DiveHitDamage; } }
        public Vector2 DiveVelocity { get { return m_DiveVelocity; } }

        public float MaxPunchComboTime { get { return m_MaxPunchComboTime; } }
        public float PunchRange { get { return m_PunchRange; } }
        public int FirstPunchDamage { get { return m_FirstPunchDamage; } }
        public int SecondPunchDamage { get { return m_SecondPunchDamage; } }
        public int ThirdPunchDamage { get { return m_ThirdPunchDamage; } }

        public float GroundCheckRayLength { get { return m_GroundCheckRayLength; } }
        public bool IsAirborne { get { return m_IsAirborne; } set { m_IsAirborne = value; } }
        public IState State { get { return m_State; } set { m_State = value; } }

        #endregion Properties

        private void Awake()
        {
            m_State = new IdleState(this);

            m_OriginPos = transform.position;
            m_CurrentCheckpoint = m_OriginPos;
        }

        private void Start()
        {
            deathUI = FindObjectOfType<UIDeathText>();
            deathUI.RespawnEvent += OnRespawn;
        }

        private void Update()
        {
            m_State.Update();

            if (m_PrintState) {
                print(m_State.ToString());
            }

            Vector3 scale = transform.localScale;
            scale.x = m_LastXDirection;
            transform.localScale = scale;

            Vector3 origin = transform.position;
            RaycastHit2D hit = Physics2D.Raycast(origin, Vector3.down, m_GroundCheckRayLength);
            Debug.DrawRay(origin, Vector3.down * m_GroundCheckRayLength, Color.red);

            if (hit && m_IsAirborne && m_Rigidbody.velocity.y < .0f) {
                m_State = new IdleState(this);
                m_IsAirborne = false;
                EventUtil.Invoke(m_GroundImpactEvent, this, EventArgs.Empty);
                m_Animator.ResetTrigger("fall");
            }
        }

        private void OnRespawn()
        {
            m_CurrentHealth = m_MaxHealth;
            transform.position = m_CurrentCheckpoint;
            EventUtil.Invoke(m_RespawnEvent, this, EventArgs.Empty);
        }
    }
}

GlideState.cs

Manages the gliding state.

 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
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace SOTDT.Gameplay.Entity.State
{
    public class GlideState : IState
    {
        private Player player;

        private float timer = .0f;

        private bool blocked = false;

        public GlideState(Player player)
        {
            this.player = player;
            player.Animator.SetTrigger("glide");

            timer = player.MaxGlideTime;
        }

        public void Update()
        {
            float xInput = Input.GetAxisRaw("Horizontal");
            float yInput = Input.GetAxisRaw("Vertical");

            Vector2 v = player.Rigidbody.velocity;

            /*
           * Dive kick
           */
            if (yInput < .0f) {
                player.State = new DiveState(player);
                return;
            }

            /*
           * Sideways movement
           */
            if (Mathf.Abs(v.x = xInput * player.GlideSpeed) > .01f) {
                player.LastXDirection = (int) Mathf.Sign(xInput);
            }

            if (Input.GetKey(KeyCode.Space) && timer > .0f) {
                player.Animator.SetTrigger("glide");

                blocked = false;

                v.y = -0.2f;

                /*
               * Sideways movement
               */
                if (Mathf.Abs(v.x = xInput * player.GlideSpeed) > .01f) {
                    player.LastXDirection = (int) Mathf.Sign(xInput);
                }

                timer -= Time.deltaTime;
            } else {
                if (!blocked) {
                    player.Animator.ResetTrigger("glide");
                    player.Animator.SetTrigger("fall");
                    v = Vector2.zero;
                    blocked = true;
                }
            }

            player.Rigidbody.velocity = v;
        }
    }
}