Shadow Of Sea
🎓 Graduation Project
About
I developed this game as my graduation project. In addition to the game development, I also wrote a thesis titled "RPG Development in Unity", covering the technical aspects of movement, combat, enemy AI, interaction, animation, and other RPG systems in Unity.
Trailer

RPG Development in Unity
View PDFCore Systems' Source Code
Movement System with State Machine
![]() |
This system controls character movement through a state-driven architecture. The StateMachine class manages transitions between states (e.g., standing, jumping, sprinting), while State serves as the base class for all states, handling input actions (movement, jump, crouch) and providing lifecycle methods (Enter, Exit, LogicUpdate, PhysicsUpdate).
StateMachine.cs
Manages transitions between states (e.g., standing → jumping) and ensures only one state is active at a time.
State.cs (Base Class)
Serves as the foundation for all concrete states (e.g., JumpingState, SprintState). It handles actions of the state (HandleInput, LogicUpdate, PhysicsUpdate) and providing lifecycle methods (Enter, Exit).
Character.cs
This class ties everything together. It initializes all states (e.g., StandingState, JumpingState) and the StateMachine,
linking them to the character’s components like the Animator and CharacterController.
In Update(), it calls the current state’s HandleInput() and LogicUpdate(),
while FixedUpdate() handles physics via PhysicsUpdate(). Shortly, It initializes all states (Standing, Jumping, Crouching, etc.) and the state machine.
Updates the current state’s input, logic and physics.
StandingState.cs : State
Default state. It detects inputs like jump, crouch, or sprint, transitioning to JumpingState, CrouchingState, or SprintState accordingly. It applies smoothed movement and rotation, aligning the character’s direction with camera input.
Other States
You can access all of the state scripts from the Github Repo
or you can access the specific state's script below.
CrouchingState.csJumpingState.cs
LandingState.cs
SprintState.cs
SprintJumpState.cs
CombatState.cs
AttackState.cs
Interaction System
Workflow:
When the player aims at an interactable, Interactor.cs calls TargetOn() from Interactable.cs, which updates the UI
via InteractableNameText.cs.
On interaction, the child classes of Interactable.cs executes overridden Interaction().
Example Scenario:
Player aims at chest -> "Press 'E'" text appears at the top of the chest -> Player presses "E" -> Chest opens.
Interactable.cs (Base Class)
The base class for all interactable objects. It defines shared properties like interaction distance and name, manages UI text visibility via InteractableNameText, and provides a virtual Interaction() method for child classes to override. Derived scripts (e.g., chests, NPCs) inherit this core logic while adding unique behaviors.
InteractableChest.cs : Interactable
Inherits from Interactable base class and manages chest-specific logic. Players can open unlocked chests to reveal loot, which triggers animations and displays a loot UI. Locked chests require a matching key from the player’s inventory to unlock. Once opened, the chest syncs with the loot UI. Reopening the UI refreshes the loot and replays animations, while closing it hides the loot and resets the chest’s state.
PickUpItem.cs : Interactable
Enables item collection. It sets the interactable’s display name from a serialized itemName field and destroys the object on interaction, simulating item pickup. Events.AddInventoryItem, allowing items to be added to the player’s inventory. This script can attach to any in-game item, from keys to consumables.
InteractableNPC.cs : Interactable
Inherits from Interactable base class and manages NPC interactions. Triggers a "wave" animation and displays a placeholder dialogue message. Built to support future dialogue systems through its interaction method.
InteractableNameText.cs
Dynamically controls UI prompts. It adjusts text based on the interactable type. For example, "[E] Open" for chest or "[E] Speak" for NPCs. It positions the text above objects using their collider bounds (e.g., placing it atop a chest’s BoxCollider). The text rotates via transform.LookAt() to face the player’s camera, ensuring readability from any angle.
Interactor.cs
Handles the player’s interaction mechanics. Using Physics.SphereCast(), it detects interactables within a set range and layer mask ("Interactable," "NPC," etc.). When the player presses the interact key (e.g., "E"), it validates the distance against the interactable’s interactionDistance and triggers its Interaction() method.
Combat System
The combat system is built around scripts that manage damage dealing, health, enemy AI, and equipment. The system relies on animation events to synchronize actions like triggering StartDealDamage() when a sword swing begins. Also system uses layer masks to filter raycast targets (player vs. enemies).
HealthSystem.cs
Serves as the foundation for player's health management. It reduces health when taking damage via TakeDamage(), triggers a "damage" animation, and shakes the camera using CameraShake. If health drops to zero, it spawns a ragdoll replacement and destroys the original object. The HitVFX() method creates visual effects at hit points, enhancing feedback during attacks.
Enemy.cs
It handles enemy behavior. Using NavMeshAgent, enemies chase the player within an aggroRange and attack when in attackRange. The enemy’s TakeDamage() method reduces health, plays hit reactions, and spawns a ragdoll on death. Enemy AI includes cooldowns (attackCD, newDestinationCD) to space out attacks and continuous player tracking for pursuit.
DamageDealer.cs and EnemyDamageDealer.cs
These scripts handle damage mechanics for enemies and the player, respectively. Both attached to the swords and use raycasts to detect hits during attacks. The enemy’s EnemyDamageDealer.cs fires a ray along its sword (layer 8 targets the player), while the player's DamageDealer targets enemies on layer 9. These scripts use flags (canDealDamage, hasDealtDamage) to ensure damage is applied only once per attack animation. They activate via StartDealDamage() and deactivate with EndDealDamage(), which are triggered by animation events.
Damage Dealer:
Enemy Damage Dealer:
EquipmentSystem.cs
It manages the player’s weapon. It draws or sheats player's sword using DrawWeapon() and SheathWeapon(), instantiating and destroying weapon models as needed.
CameraShake.cs
Adds screen-shake effects using Cinemachine. When called it triggers a shake by modifying the CinemachineBasicMultiChannelPerlin noise component of a virtual camera.
The ShakeCamera() method sets the shake strength (m_AmplitudeGain) and duration,
while Update() smoothly interpolates the intensity back to zero over time using Mathf.Lerp(), creating a natural fade-out effect.
Designed as a singleton (Instance), it can be accessed globally, making it easy to trigger shakes from any script (e.g., when taking damage).