Forum begins after the advertisement:


[General] Creating a King Bible Weapon

Home Forums Video Game Tutorial Series Creating a Rogue-like Shoot-em Up in Unity [General] Creating a King Bible Weapon

Viewing 5 posts - 1 through 5 (of 5 total)
  • Author
    Posts
  • #16051
    kyle
    Level 9
    Moderator
    Helpful?
    Up
    0
    ::
    Update 15 October 2024 – Fixed KingBibleWeapon.cs being incomplete, thanks to user Grim Rubbish for pointing this out!
    Hi all! Recently, viewer NHZC suggested in the comments of one of the Vampire Survivors tutorials that we make a forum post implementing the King Bible into our game, so here’s a step-by-step guide on creating a basic King Bible Weapon. A showcase of the King Bible weapon we created destroying some evil flowers. A showcase of the King Bible weapon we created destroying some evil flowers.

    Before we start, ensure that you’ve completed the series all the way up to Creating a Rogue-like (like Vampire Survivors) in Unity — Part 18: Implementing a Dynamic Stats System and UI, as we’ve made major improvements to it such as character stats affecting the weapon stats and much more.

    Here’s what we will be covering.

    1. Creating the King Bible Prefab 2. Creating the KingBibleWeapon.cs script 3. Creating the King Bible Weapon Data 4. Creating the King Bible Character (Dommario) 5. Changing the Weapon.cs and Projectile.cs script 6. Creating the KingBibleProjectile.cs script

    1. Creating the King Bible Prefab.

    To start off, let’s first make the Bible Prefab and add its VFX.
    1. Create an empty GameObject in your scene and name it “King Bible Projectile”. Add a Sprite Renderer component to it and drag the sprite you’d like to use for your Bible (I used Weapon Sprite Sheet_47 from the default Weapon Spritesheet in our project.)
    2. Next, add a Particle System component. We’ll recreate the pages falling out effect from the original game.
    3. For the first few settings, here’s the suggested configurations. Of course, you can tweak these values however you want.
      • Duration – 3
      • Looping – on
      • Start Lifetime – 0.3
      • Start Speed – 1
      • Start Size – 0.75
      • Start Rotation – 15
      • Simulation Space – World (Allows the particles to move freely around after being emitted)
      • Scaling Mode – Hierarchy (Scales the particles with the scale of the Game Object)
    4. Next, we’ll go over to “Emission” and set the Rate over Time to 3.5.
    5. Going onto “Shape”, we’ll set the Shape to Edge, set the Radius to 0.0001, and set the Z-rotation to 45.
    6. After that, we’ll apply a fade over time using “Color over Lifetime”. Click on the Gradient Editor and set the Alpha at the end to 0.
    1. To replace the default particle with the falling page sprite, let’s tick “Texture Sheet Animation” and drop it down. We’ll set the mode to Sprites and add the ‘Paper’ sprite into the slot.
    2. To end off the VFX, go to “Renderer” and configure these following settings. Remember to also configure the Order in Layer properly so the particles do not get blocked!
      • Min Particle Size – 0
      • Max Particle Size – 99
      • Render Alignment – Local
    3. After that, add a Rigidbody2D component, and set the Body Type to Kinematic. Finally, add a Box Collider, check the Is Trigger box, and resize it to fit the sprite.
    You can then drag the finished GameObject in your Weapon Prefabs folder to create the King Bible prefab. Here’s how your Prefab should look like after you’re done.
    View post on imgur.com
    How my King Bible Projectile prefab was set up

    2. Creating the KingBibleWeapon.cs script.

    Here, we’ll set up the controller that will fire the projectiles. We’re going to create a new C# script called KingBibleWeapon.cs. Here’s the script with an explanation further below:
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class KingBibleWeapon : ProjectileWeapon
    {
        public void SpawnRing(PlayerStats player = null)
        {
            // Only activate this if the player is present.
            if (player)
            {
                float angleOffset = 2 * Mathf.PI / Mathf.Max(1, currentStats.number); // 2 * Mathf.PI = 360. We are just calculating how far of an angle to space out the prefabs from each other on a circle's circumference.
                float currentAngle = 0;
                for (int i = 0; i < currentStats.number; i++)
                {
                    // Convert the spawn angle onto a point on the circle's circumference and space it away relative to the player.
                    Vector3 spawnPosition = player.transform.position + new Vector3(
                        GetArea() * Mathf.Cos(currentAngle) ,
                        GetArea() * Mathf.Sin(currentAngle)
                    );
    
                    // Spawn the book at the calculated position, and parents it to the owner so it follows them around.
                    Projectile prefab = Instantiate(currentStats.projectilePrefab, spawnPosition, Quaternion.identity,owner.transform);
    
                    prefab.owner = owner;
                    currentAngle += angleOffset;
                    prefab.weapon = this;
                }
            }
        }
        protected override bool Attack(int attackCount = 1)
        {
            // If no projectile prefab is assigned, leave a warning message.
            if (!currentStats.projectilePrefab)
            {
                Debug.LogWarning(string.Format("Projectile prefab has not been set for {0}", name));
                ActivateCooldown(true);
                return false;
            }
    
            // Can we attack?
            if (!CanAttack()) return false;
    
            SpawnRing(owner);
    
            ActivateCooldown(true);
    
            attackCount--;
    
            // Do we perform another attack?
            if (attackCount > 0)
            {
                currentAttackCount = attackCount;
                currentAttackInterval = ((WeaponData)data).baseStats.projectileInterval;
            }
    
    
            return true;
        }
    
        public override bool ActivateCooldown(bool strict = false)
        {
            // When 'strict' is enabled and the cooldown is not yet finished,
            // do not refresh the cooldown.
            if (strict && currentCooldown > 0) return false;
    
            // Calculate what the cooldown is going to be, factoring in the cooldown
            // reduction stat in the player character.
            // Cooldown is dependent on lifespan, so we add it.
            float actualCooldown = (currentStats.lifespan + currentStats.cooldown) * Owner.Stats.cooldown;
    
            // Limit the maximum cooldown to the actual cooldown, so we cannot increase
            // the cooldown above the cooldown stat if we accidentally call this function
            // multiple times.
            currentCooldown = Mathf.Min(actualCooldown, currentCooldown + actualCooldown);
            return true;
        }
    
        public override bool DoLevelUp()
        {
            base.DoLevelUp();
            // Spawn a ring after every upgrade option, just like in the original game.
            SpawnRing(owner);
            ActivateCooldown(false);
            return true;
        }
    }
    
    • We’re going to make it inherit from the ProjectileWeapon class, since it’s a weapon that uses Projectiles. This means we can override some functions to make the King Bible work as intended.

    The SpawnRing() function

    • This function spawns the evenly spaced out King Bible projectiles in a circle. There is some math involved, so here’s a quick rundown of the function:
      1. First off, we’ll check if there’s a player we can attach the spawned projectiles to.
      2. We calculate angleOffset to find out the space we need between each projectile.
      3. After that, we get the distance from the Player’s transform to the point on the circle’s circumference where the projectile is spawned using Mathf.Cos(currentAngle) and Mathf.Sin(currentAngle) and multiplying each of them by the radius (GetArea()).
      4. With the math done, we instantiate the assigned prefab at the calculated spawnPosition, and set its parent to be the Player who owns the weapon, so it follows them wherever they go.
      5. We then increment the currentAngle by the angleOffset to space the projectiles evenly, and repeat the process until the correct amount of projectiles has been spawned.

    How the Math in SpawnRing() works…

    If you want to know how the circular spawning in SpawnRing() works, here’s how. Although it looks a bit daunting, if you have done basic trigonometry before, it’ll be easy to follow through.
    • The angleOffset formula is 2 * Pi divided by the amount of projectiles the weapon spawns. 2 * Pi is just a way of representing 360° in radian form.
    • If we have 3 projectiles, the angleOffset of each projectile in degrees would be 360°/3 = 120°. In radian form, 120° is about 2.094 radians.
    • angleOffset needs to be in radians as the next functions we’ll use, Mathf.Cos() and Mathf.Sin() take in radian values only.
    • Here’s where the trigonometry comes in. The explanation is quite long, so you can check this illustrated Imgur gallery out!
    View post on imgur.com

    The Overriden Attack() function

    • The main thing we’ve changed is instead of just spawning 1 projectile prefab, we make it call the SpawnRing() function instead to spawn multiple King Bibles. We’ve also removed the GetSpawnAngle() call as the projectiles aren’t shot-based.

    The Overriden ActivateCooldown() function

    • We’ve overriden the ActivateCooldown() function to factor in the lifespan of the projectile, since according to its Vampire Survivors Wiki Page, its cooldown is extended by its duration (lifespan).
    • Because of this, you’ll notice if your duration stat is high enough, you’ll be able to spawn more than one layer of King Bibles at once, which also happens in the original game!

    The Overriden DoLevelUp() function

    • If you’ve noticed in the original game, whenever you upgrade the King Bible, a new ring with the new stats spawns, so we’ll just spawn in a new ring with SpawnRing() and reset the cooldown afterwards.

    3. Creating the King Bible Weapon Data

    Here, we’ll set up the Weapon Data Scriptable Object that will give our King Bible its stats.
    1. Navigate to your Scriptable Objects>Weapons folder, right-click and go to Create>2D Top-Down Rogue-Like>Weapon Data. Rename the newly created Scriptable Object to “King Bible”.
    2. Set the Behaviour to “KingBibleWeapon” and you should be able to configure your King Bible accordingly. Here’s the wiki page for it if you’d like to have the exact stats.
    Here’s how I configured my King Bible Weapon Data:
    https://imgur.com/a/bRftml4 How my King Bible Weapon Data was set up

    4. Creating the King Bible Character (Dommario)

    Now that we’ve got our King Bible’s Weapon Data set, we can create the character that mains it.
    1. Go to your Scriptable Objects>Characters folder, right-click and go to Create>2D Top-Down Rogue-Like>Character Data.
    2. Configure the character by assigning its Starting Weapon to be the “King Bible” scriptable object, and setting the stats accordingly. If you’d like to configure the character according to his Vampire Survivors counterpart, you can refer to his Wiki page.
    Here’s how I configured the King Bible Character (Dommario): The stats I used for my King Bible Character (Dommario)

    5. Changing the Projectile.cs and Weapon.cs script

    Before we move on to adding a Projectile script for the King Bible, we need to modify the Weapon.cs and Projectile.cs scripts.

    Changes to the Weapon.cs script

    Just copy and paste these 2 functions into your Weapon.cs script.
        public virtual float GetSpeed()
        {
            return currentStats.speed * owner.Stats.speed;
        }
        public virtual float GetLifespan()
        {
            return currentStats.lifespan * owner.Stats.duration;
        }
    
    • The new functions, GetSpeed() and GetLifespan(), retrieve the speed and lifespan stats multiplied by any stat boosts that the owner has.

    Changes to the Projectile.cs script

    using UnityEngine;
    
    /// <summary>
    /// Component that you attach to all projectile prefabs. All spawned projectiles will fly in the direction
    /// they are facing and deal damage when they hit an object.
    /// </summary>
    [RequireComponent(typeof(Rigidbody2D))]
    public class Projectile : WeaponEffect
    {
    
        public enum DamageSource { projectile, owner };
        public DamageSource damageSource = DamageSource.projectile;
        public bool hasAutoAim = false;
        public Vector3 rotationSpeed = new Vector3(0, 0, 0);
    
        protected Rigidbody2D rb;
        protected int piercing;
        protected float area;
    
        // Start is called before the first frame update
        protected virtual void Start()
        {
            rb = GetComponent<Rigidbody2D>();
            Weapon.Stats stats = weapon.GetStats();
            if (rb.bodyType == RigidbodyType2D.Dynamic)
            {
                rb.angularVelocity = rotationSpeed.z;
                rb.velocity = transform.right * weapon.GetSpeed();
            }
    
            // Prevent the area from being 0, as it hides the projectile.
            area = weapon.GetArea();
            if (area <= 0) area = 1;
            transform.localScale = new Vector3(
                area * Mathf.Sign(transform.localScale.x),
                area * Mathf.Sign(transform.localScale.y), 1
            );
    
            // Set how much piercing this object has.
            piercing = stats.piercing;
    
            // Destroy the projectile after its lifespan expires.
            if (weapon.GetLifespan() > 0) Destroy(gameObject, weapon.GetLifespan());
    
            // If the projectile is auto-aiming, automatically find a suitable enemy.
            if (hasAutoAim) AcquireAutoAimFacing();
        }
    
        // If the projectile is homing, it will automatically find a suitable target
        // to move towards.
        public virtual void AcquireAutoAimFacing()
        {
            float aimAngle; // We need to determine where to aim.
    
            // Find all enemies on the screen.
            EnemyStats[] targets = FindObjectsOfType<EnemyStats>();
    
            // Select a random enemy (if there is at least 1).
            // Otherwise, pick a random angle.
            if (targets.Length > 0)
            {
                EnemyStats selectedTarget = targets[Random.Range(0, targets.Length)];
                Vector2 difference = selectedTarget.transform.position - transform.position;
                aimAngle = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
            }
            else
            {
                aimAngle = Random.Range(0f, 360f);
            }
    
            // Point the projectile towards where we are aiming at.
            transform.rotation = Quaternion.Euler(0, 0, aimAngle);
        }
    
        // Update is called once per frame
        protected virtual void FixedUpdate()
        {
            // Only drive movement ourselves if this is a kinematic.
            if (rb.bodyType == RigidbodyType2D.Kinematic)
            {
                Weapon.Stats stats = weapon.GetStats();
                transform.position += transform.right * stats.speed * weapon.Owner.Stats.speed * Time.fixedDeltaTime;
                rb.MovePosition(transform.position);
                transform.Rotate(rotationSpeed * Time.fixedDeltaTime);
            }
        }
    
        protected virtual void OnTriggerEnter2D(Collider2D other)
        {
            EnemyStats es = other.GetComponent<EnemyStats>();
            BreakableProps p = other.GetComponent<BreakableProps>();
    
            // Only collide with enemies or breakable stuff.
            if (es)
            {
                // If there is an owner, and the damage source is set to owner,
                // we will calculate knockback using the owner instead of the projectile.
                Vector3 source = damageSource == DamageSource.owner && owner ? owner.transform.position : transform.position;
    
                // Deals damage.
                es.TakeDamage(GetDamage(), source);
    
                // Get the weapon's stats.
                Weapon.Stats stats = weapon.GetStats();
    
                weapon.ApplyBuffs(es); // Apply all assigned buffs to the target.
    
                // Reduce the piercing value, and destroy the projectile if it runs of out of piercing.
                piercing--;
                if (stats.hitEffect)
                {
                    Destroy(Instantiate(stats.hitEffect, transform.position, Quaternion.identity), 5f);
                }
            }
            else if (p)
            {
                p.TakeDamage(GetDamage());
                piercing--;
    
                Weapon.Stats stats = weapon.GetStats();
                if (stats.hitEffect)
                {
                    Destroy(Instantiate(stats.hitEffect, transform.position, Quaternion.identity), 5f);
                }
            }
    
            // Destroy this object if it has run out of health from hitting other stuff.
            if (piercing <= 0) Destroy(gameObject);
        }
    }
    
    • We’ve moved the area variable out of Start() and made it protected, so we can use it outside of  base.Start() in any inherited classes.
    • We’ve replaced stats.speed and stats.lifespan with the GetSpeed() and GetLifespan() functions.

    6. Creating the KingBibleProjectile.cs script.

    Now, all that’s left is to create a script to give our King Bible Projectiles functionality. Since the King Bible is quite a unique projectile, we’ll be overriding and modifying quite a bit of code. Here’s the script with an explanation further below.
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class KingBibleProjectile : Projectile
    {
        Dictionary<EnemyStats, float> affectedTargets = new Dictionary<EnemyStats, float>();
        List<EnemyStats> targetsToUnaffect = new List<EnemyStats>();
        public float hitDelay = 1.7f;
    
        //We multiply this with the base speed, as the default values by themselves rotate too slowly.
        public float speedMultiplier = 5f; 
    
        // We need these multipliers for area since we scale the radius and projectile size differently.
        // If we don't, we will have really big books as the radius increases which is not the case in-game.
        public float radiusMultiplier = 1.1f; 
        public float projectileSizeMultiplier = 0.9f;
    
        //How much time does it take for the projectile to grow/shrink to its needed size
        public float transitionTime = 0.5f;
    
        private float currentLifespan;
    
        Vector3 startScale;
        float startLifespan;
        bool isAlive = false;
        private float angle;
    
        // Start is called before the first frame update
        protected override void Start()
        {
            base.Start();
            startScale = new Vector3(area, area,1);
            startLifespan = weapon.GetLifespan();
            transform.localScale = Vector3.zero;
            StartCoroutine(BibleGrow());
            Vector3 offset = transform.position - owner.transform.position; //Get the initial spawn position
            angle = Mathf.Atan2(offset.y, offset.x); // Get the angle in radians
        }
    
        protected override void FixedUpdate()
        {
            if (rb && rb.bodyType == RigidbodyType2D.Kinematic)
            {
                float x = owner.transform.position.x + Mathf.Cos(angle) * startScale.x * radiusMultiplier;
                float y = owner.transform.position.y + Mathf.Sin(angle) * startScale.y * radiusMultiplier;
                rb.MovePosition(new Vector3(x, y, 0));
                angle -= weapon.GetStats().speed * speedMultiplier * Time.fixedDeltaTime;
            }
        }
        private void Update()
        {
            HitDelay();
    
            currentLifespan += Time.deltaTime;
    
            if (!rb)
            {
                float x = owner.transform.position.x + Mathf.Cos(angle) * startScale.x * radiusMultiplier;
                float y = owner.transform.position.y + Mathf.Sin(angle) * startScale.y * radiusMultiplier;
                transform.position = new Vector3(x, y, 0);
                angle -= weapon.GetStats().speed * speedMultiplier * Time.deltaTime;
            }
    
            if (currentLifespan > startLifespan-transitionTime && isAlive)
            {
                StartCoroutine(BibleShrink());
                isAlive = false;
            }
            if (!weapon && isAlive)
            {
                StartCoroutine(BibleShrink());
                isAlive = false;
            }
        }
        public void HitDelay()
        {
            Dictionary<EnemyStats, float> affectedTargsCopy = new Dictionary<EnemyStats, float>(affectedTargets);
    
            // Loop through every target that has been hit by this projectile, and reduce the cooldown
            // of the projectile for it. If the cooldown reaches 0, deal damage to it.
            foreach (KeyValuePair<EnemyStats, float> pair in affectedTargsCopy)
            {
                if (pair.Key)
                {
                    Vector3 source = damageSource == DamageSource.owner && owner ? owner.transform.position : transform.position;
                    affectedTargets[pair.Key] -= Time.deltaTime;
                    if (pair.Value <= 0)
                    {
                        if (targetsToUnaffect.Contains(pair.Key))
                        {
                            // If the target is marked for removal, remove it.
                            affectedTargets.Remove(pair.Key);
                            targetsToUnaffect.Remove(pair.Key);
                        }
                        else
                        {
                            // Reset the cooldown and deal damage.
                            Weapon.Stats stats = weapon.GetStats();
                            affectedTargets[pair.Key] = hitDelay;
                            pair.Key.TakeDamage(GetDamage(), source, stats.knockback);
    
                            weapon.ApplyBuffs(pair.Key); // Apply all assigned buffs to the target.
    
                            // Play the hit effect if it is assigned.
                            if (stats.hitEffect)
                            {
                                Destroy(Instantiate(stats.hitEffect, pair.Key.transform.position, Quaternion.identity).gameObject, 5f);
                            }
                            piercing--;
                        }
                    }
                }
    
            }
        }
    
        public IEnumerator BibleShrink()
        {
            Vector3 currentScale = transform.localScale;
    
            // Waits for a single frame.
            WaitForEndOfFrame w = new WaitForEndOfFrame();
            float t = 0;
    
            // This is a loop that fires every frame.
            while (t < transitionTime)
            {
                yield return w;
                t += Time.deltaTime;
    
               // Reduce the current size to 0 within transitionTime
               transform.localScale = new Vector3(currentScale.x - (t/ transitionTime), currentScale.y - (t / transitionTime), 1f);
            }
            if (!weapon) Destroy(gameObject);
        }
        public IEnumerator BibleGrow()
        {
            if (!isAlive)
            {
                isAlive = true;
                // Waits for a single frame.
                WaitForEndOfFrame w = new WaitForEndOfFrame();
                float t = 0;
    
                // This is a loop that fires every frame.
                while (t < transitionTime)
                {
                    yield return w;
                    t += Time.deltaTime;
    
                    // Grow the size from 0 to the scale from the weapon's area multiplied by the projectileSizeMultiplier, within the transitionTime.
                    transform.localScale = new Vector3(0 + (t/ transitionTime * startScale.x) * projectileSizeMultiplier, 0 + (t/ transitionTime * startScale.y) * projectileSizeMultiplier, 1f);
                }
    
            }
    
    
            //Destroy(gameObject);
        }
    
        protected override void OnTriggerEnter2D(Collider2D other)
        {
            if (other.TryGetComponent(out EnemyStats es))
            {
                // If the target is not yet affected by this bible, add it
                // to our list of affected targets.
                if (!affectedTargets.ContainsKey(es))
                {
                    // Always starts with an interval of 0, so that it will get
                    // damaged in the next Update() tick.
                    affectedTargets.Add(es, 0);
                }
            }
            else if (other.TryGetComponent(out BreakableProps p))
            {
                p.TakeDamage(GetDamage());
                piercing--;
    
                Weapon.Stats stats = weapon.GetStats();
                if (stats.hitEffect)
                {
                    Destroy(Instantiate(stats.hitEffect, transform.position, Quaternion.identity).gameObject, 5f);
                }
            }
            // Destroy this object if it has run out of health from hitting other stuff.
            if (piercing <= 0) Destroy(gameObject);
        }
    }
    
    
    • We’re going to make it inherit from the Projectile.cs class so we can easily change the core functionality through overriding.

    Variable Overview

    • affectedTargets and targetsToUnaffect – Used in implementing a hit delay system.
    • hitDelay – Used to prevent enemies from being hit by the same Bible projectile while the delay is active.
    • speedMultiplier – Multiplied with the speed stat to control how fast the Bible projectiles revolves around the player. Without this stat, the base speed of 1 is too slow for a revolution.
    • radiusMultiplier and projectileSizeMultiplier – We apply these multipliers to the area stat to control the distance of the Bibles from the Player and the Bible size respectively. We need different multipliers for each as having the same scaling for both radius and Bible size would lead to really big Bibles further down the upgrade chain.
    • transitionTime is how long it takes for the Bible projectiles to scale up to their size/shrink and disappear during the spawning and lifespan ending phases respectively.

    The overriden Start() function

    • In the Start() function, we call base.Start(). What this does is it runs the Start function in the script we’re inheriting from (Projectile.cs), since it helps us initialise variables like Rigidbody rb which is used to determine the type of movement.
    • After that, we zero out the transform.localScale of the projectile since we’re going to make it appear by calling the BibleGrow() coroutine.
    • Finally, we calculate the angle variable, which is used to make sure that the projectiles are offset properly when they start spinning around.

    The overriden FixedUpdate() function

    • Since the King Bibles are stationary projectiles that just hover around, we’ll override the original code that makes it fly in one direction and instead replace it with a Rigidbody.MovePosition() that moves the projectile in a circle.
    • We’ve also modified the circular movement code here to use Time.fixedDeltaTime since we are in FixedUpdate.

    The overriden Update() function

    • A HitDelay() function is implemented to prevent the same bible from hitting an enemy too often.
    • If there is no Rigidbody2D attached to the projectile, we move it using the transform.position instead.
    • We have 2 conditions that control the calling of the BibleShrink() coroutine. One occurs if the Bible Projectile is about to end its lifespan, while the other occurs if the King Bible Weapon Controller is destroyed (i.e. the Weapon has evolved).

    The HitDelay() function

    • This works similarly to the Aura.cs script that we implemented a while back, where it stores an enemy on contact, keeps it in a Dictionary and pairs it with a value, and prevents the enemy from receiving damage until that value has depleted.
    • The only difference is that it doesn’t require the OnTriggerExit2D(), since it is hit-based instead of area-of-effect.

    The BibleGrow() coroutine

    • This coroutine is only called once in Start().
    • Scales the Bible projectile up within the transitionTime.

    The BibleShrink() coroutine

    • Shrinks the bible down within the transitionTime.
    • The Bible gets destroyed through the Destroy() function in base.Start().
    • If the coroutine was called because the Weapon Controller was destroyed, we just destroy the projectile as soon as it ends regardless of the lifespan remaining.

    The overriden OnTriggerEnter2D() function

    • Overriden to allow enemies that have been hit be affected by the HitDelay() function.
    Once you’ve gotten the script, you can attach it to your King Bible Prefab, oh and wait…

    Before You Test…

    Remember to add the King Bible Weapon Data into the “Available Weapons” section of the PlayerInventory, or else it won’t appear in the shop.

    Now, you should be able to have a basic, working King Bible that can rotate, spawn. Since you’ve already got the weapon, you can check out how we implemented its evolution, the Unholy Vespers, here!

    As always, if you encounter any problems in creating the King Bible, you can submit a forum post about in detail for further assistance. If you have your own implementation of the King Bible, or have any improvements you’d want to see, feel free to make a post as well!

    #16074
    Grim Rubbish
    Level 16
    Silver Supporter (Patron)
    Helpful?
    Up
    1
    ::

    Thanks for this new weapon! The “KingBibleWeapon.cs” seems to create some errors, there are two missing “}” on lines 24 and 25. Also “float currentAngle” is never used and line 17 “currentAttackCount = attackCount;” creates an error. I’m currently at Part 23: Buff / Debuff System – maybe I miss something… ;)

    has upvoted this post.
    #16079
    kyle
    Level 9
    Moderator
    Helpful?
    Up
    0
    ::

    Hi Grim, no problem, Thanks for highlighting this issue! Part of the “KingBibleWeapon.cs” code got cut off when I copied and pasted the script onto here so I added in the missing parts. Hopefully it works now, it was I who missed something :D.

    #16084
    Grim Rubbish
    Level 16
    Silver Supporter (Patron)
    Helpful?
    Up
    1
    ::

    Thank you so much Kyle, it works perfectly now! :)

    has upvoted this post.
    #16092
    kyle
    Level 9
    Moderator
    Helpful?
    Up
    0
    ::

    No problem, glad to hear it worked!

Viewing 5 posts - 1 through 5 (of 5 total)
  • You must be logged in to reply to this topic.

Go to Login Page →


Advertisement below: