Hello fellow developers and RPG enthusiasts!
I'm thrilled to announce a major update to the 2D RPG Project Accelerator available on the Unity Asset Store. As an indie game developer myself, I've been hard at work refining and expanding the features of this package to make it even more powerful and user-friendly for creators like you.
Today, I'm excited to unveil the newly implemented Attack System, which brings a new level of depth and flexibility to your game's combat mechanics. This update not only enhances gameplay but also showcases robust coding practices and architectural improvements that you can leverage in your own projects.
What's New in the Attack System?
1. Unified Damage Calculation Function
We've merged multiple damage calculation functions into a single, versatile method that handles both humanoid and non-humanoid characters seamlessly.
Polymorphic Design: By utilizing inheritance and polymorphism, the
CalculateDamage
function now accepts aBaseMobile
parameter, allowing all mobile entities to calculate damage appropriately based on their specific attributes.public static class DamageHelper { // Calculate the final damage dealt by the mobile to a target public static int CalculateDamage(BaseMobile mobile, ICombatant target) { var stats = mobile.Stats; // Calculate base bare hands damage based on Strength float bareHandsDamage = stats.Strength * 0.5f; // Bare hands damage scales with Strength // Initialize baseDamage with bare hands damage float baseDamage = bareHandsDamage; // Check if the mobile is a humanoid with equipment if (mobile is BaseHumanoid humanoid) { var equipment = humanoid.Equipment; // Get the equipped weapon (if any) var weapon = equipment.GetEquippedItem(EquipmentType.MainHand) as BaseWeapon; if (weapon != null) { // Weapon damage: base weapon damage + some scaling with Strength baseDamage = weapon.Damage + (stats.Strength * 0.3f); // Weapons get a moderate bonus from Strength } else { // Bare hands damage: scales more directly with Strength baseDamage = bareHandsDamage; } } // Add randomness to the damage (±10% of the base damage) float randomFactor = UnityEngine.Random.Range(0.9f, 1.1f); // Random variation between 90% and 110% float resultedDamage = baseDamage * randomFactor; // Ensure there's at least 1 damage dealt int finalDamage = Mathf.CeilToInt(Mathf.Max(resultedDamage, 1)); // Return the final damage return finalDamage; } }
Humanoid-Specific Logic: Within the same function, we check if the mobile is a
BaseHumanoid
to apply weapon-based damage calculations.if (mobile is BaseHumanoid humanoid) { // Humanoid-specific damage calculation, including weapon stats }
Benefits: This approach reduces code duplication, enhances maintainability, and makes it easier to extend functionality in the future.
2. Flexible Attack Speed Mechanics
We've overhauled how attack speed is calculated to make it more dynamic and responsive to character stats.
Attribute-Based Modifiers: Attack speed now considers Dexterity and Stamina, affecting how quickly a character can perform attacks.
public static float GetFinalAttackSpeed(BaseMobile mobile) { float dexterityMultiplier = GetDexterityMultiplier(mobile); float staminaMultiplier = GetStaminaMultiplier(mobile); // Set a minimum base speed so that the attack is never too slow float baseAttackSpeed = 1.5f; // Initialize final attack speed calculation float finalAttackSpeed = baseAttackSpeed * dexterityMultiplier * staminaMultiplier; // Check if the mobile is a humanoid to apply weapon attack speed modifier if (mobile is BaseHumanoid humanoid) { float weaponAttackSpeedModifier = GetWeaponAttackSpeedModifier(humanoid); finalAttackSpeed *= weaponAttackSpeedModifier; } // Clamp the final speed between 0.5x and 3.0x return Mathf.Clamp(finalAttackSpeed, 0.5f, 3f); }
Extensible Design: The system is designed to accommodate additional character types and modifiers with minimal changes.
3. Refactored Mobile Behaviour Classes
To better support diverse character types, we've refactored the MobileBehaviour
and HumanoidBehaviour
classes.
Generic
MobileBehaviour
: TheMobileBehaviour
class now works with a generalBaseMobile
type instead of being tied toBaseHumanoid
.public class MobileBehaviour : MonoBehaviour { protected BaseMobile mobile; // ... }
Humanoid-Specific Behaviour:
HumanoidBehaviour
inherits fromMobileBehaviour
and uses casting to access humanoid-specific properties.public class HumanoidBehaviour : MobileBehaviour { public BaseHumanoid Humanoid => (BaseHumanoid)mobile; // ... }
Technical Challenges Overcome:
Unity's Generic Limitations: Unity doesn't support
GetComponent
with generic types, so we've carefully designed the classes to work within these constraints.Type Safety: By using casting and property overrides, we maintain type safety while ensuring the code remains clean and understandable.
4. Lootable Enemies with Shared Inventory System
All mobile entities now have inventories, making it possible to loot any defeated enemy.
Inventory Integration: Moved inventory management from
HumanoidBehaviour
toMobileBehaviour
, allowing all mobiles to carry items.[RequireComponent(typeof(InventoryBehaviour))] public class MobileBehaviour : MonoBehaviour { protected InventoryBehaviour inventoryBehaviour; // ... }
Why These Changes Matter
For Developers:
Cleaner Architecture: The refactoring leads to a more organized codebase that's easier to navigate and extend.
Reusability: By generalizing mobile behaviors, you can add new character types without rewriting common logic.
Performance Optimizations: Efficient use of inheritance and method merging reduces overhead and potential bugs.
For Players:
Enhanced Gameplay Experience: Players can now enjoy more realistic combat mechanics and the satisfaction of looting any enemy they defeat.
Strategic Depth: The impact of character stats on combat introduces new layers of strategy and customization.
Technical Insights
Overcoming Unity's Component Limitations
Unity doesn't fully support generics in components that need to be serialized or accessed via GetComponent
. To address this:
Avoided Generics in Components: Kept
MobileBehaviour
non-generic and used casting inHumanoidBehaviour
to access specific properties.Property Shadowing: Used the
new
keyword to shadow theMobile
property inHumanoidBehaviour
, allowing access toBaseHumanoid
without altering the base class.
Efficient Use of Polymorphism
Single Responsibility Principle: Each class has a clear purpose—
MobileBehaviour
handles general mobile logic, whileHumanoidBehaviour
manages humanoid-specific features.Extensibility: This structure makes it straightforward to introduce new mobile types like animals or monsters by creating new subclasses of
MobileBehaviour
.
Refined Damage and Attack Speed Calculations
Unified Functions: Merged separate functions for humanoids and other mobiles to reduce redundancy.
Dynamic Calculations: Stats and equipment now dynamically affect combat outcomes, making the system more flexible and realistic.
What's Next for the Package?
We're committed to continuously improving the 2D RPG Project Accelerator. Upcoming features include:
Expanded Item System: Introduction of off-hand items (shields), two-handed weapons, and crafting mechanics.
Enhanced AI: Smarter enemy behaviors and more engaging combat scenarios.
Skill Systems: Allowing characters to learn and use unique abilities, further deepening gameplay.
Get the Updated Package Today!
If you're looking to accelerate your game development with a robust, flexible, and well-architected system, this update is for you.
New Buyers: Purchase the 2D RPG Project Accelerator on the Unity Asset Store and get access to all these features and future updates.
Existing Customers: Simply download the latest version to enjoy the new attack system and improvements.
Visit the Unity Asset Store to get started!
Your Feedback Matters
As an indie developer, I value your input immensely. If you have suggestions, encounter issues, or want to share how you're using the package in your projects, please don't hesitate to reach out.
Let's build amazing games together!
Keep rocking!
Deniz