Jump to content
  • Advertisement
  • entries
    44
  • comments
    19
  • views
    2424

100 Days of VR: Day 18 Creating Weapon Ammo in Unity

Josh Chang

604 views

Today in Day 18, we’re going to start adding UI components into our game!

2 days ago, on Day 16, we created a reload system and just yesterday we started shooting lasers at enemies, now would be the perfect time to create a reload system and some UI to display our reload.

While we’re at it, we might as well make some other UI fixes. Here are the things that we’re going to tackle today:

  • Create the reload system and UI

The first thing that needs to be done is to create the reload system.

Without any delays, let’s get started!

Creating the Reload System

There are 2 things that need to be done for us to have a reload system. The first part is the UI that shows how many ammos we have and the second is the code that will manage it.

Let’s create the UI system first.

Player Ammo UI

First thing to do, let’s find a motivation to for our UI.

For my source of inspiration, I’m going to use Overwatch, which I think is pretty common these days:

 
0*Ka2hf9ALShsDqgc5.jpg

The basic thing is that the HP is on the bottom left corner and the ammo is on the bottom right.

Let’s get started creating the ammo UI. In the hierarchy pane, right click and under UI select Text. We’re going to call it Ammo.

As you might recall, when we do this, Unity automatically creates a Canvas screen for us. In this case, we already have one that called HUD.

To work with our UI, make sure to hit 2D button right below the Scene tab.

 
0*ItATTM3OyBH5YZrI.png

Let’s adjust the text to be at the bottom right corner of our screen.

Select Ammo and under the Rect Transform component, select the anchor presets square and while holding Shift + Alt, select bottom right, to move our text to the bottom right corner.

It’s also kind of small, so let’s change the size a bit too:

  • Width: 160
  • Height: 60
  • Text Size: 24
  • Font Style: Bold
  • Text: 30/30
  • Color: White

Here’s what we’ll see now if we pull up our game tab:

 
0*AsWLT8-q6IdfuUDL.png

Now that we have our initial setup let’s write code!

Ammo and Reload Code

We have the UI, now it’s time to write some code for reloading and ammo!

We’ll be changing our PlayerShootingController, here’s the code:

 
using UnityEngine;
using System.Collections;
public class PlayerShootingController : MonoBehaviour
{
    public float Range = 100;
    public float ShootingDelay = 0.1f;
    public AudioClip ShotSfxClips;
    public Transform GunEndPoint;
    public float MaxAmmo = 10f;
    private Camera _camera;
    private ParticleSystem _particle;
    private LayerMask _shootableMask;
    private float _timer;
    private AudioSource _audioSource;
    private Animator _animator;
    private bool _isShooting;
    private bool _isReloading;
    private LineRenderer _lineRenderer;
    private float _currentAmmo;

    void Start () {
        _camera = Camera.main;
        _particle = GetComponentInChildren<ParticleSystem>();
        Cursor.lockState = CursorLockMode.Locked;
        _shootableMask = LayerMask.GetMask("Shootable");
        _timer = 0;
        SetupSound();
        _animator = GetComponent<Animator>();
        _isShooting = false;
        _isReloading = false;
        _lineRenderer = GetComponent<LineRenderer>();
        _currentAmmo = MaxAmmo;
    }
    
    void Update ()
    {
        _timer += Time.deltaTime;
        // Create a vector at the center of our camera's viewport
        Vector3 lineOrigin = _camera.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 0.0f));
        // Draw a line in the Scene View  from the point lineOrigin in the direction of fpsCam.transform.forward * weaponRange, using the color green
        Debug.DrawRay(lineOrigin, _camera.transform.forward * Range, Color.green);
        if (Input.GetMouseButton(0) && _timer >= ShootingDelay && !_isReloading && _currentAmmo > 0)
        {
            Shoot();
            if (!_isShooting)
            {
                TriggerShootingAnimation();
            }
        }
        else if (!Input.GetMouseButton(0))
        {
            StopShooting();
            if (_isShooting)
            {
                TriggerShootingAnimation();
            }
        }
        if (Input.GetKeyDown(KeyCode.R))
        {
            StartReloading();
        }
    }
    private void StartReloading()
    {
        _animator.SetTrigger("DoReload");
        StopShooting();
        _isShooting = false;
        _isReloading = true;
    }
    private void TriggerShootingAnimation()
    {
        _isShooting = !_isShooting;
        _animator.SetTrigger("Shoot");
    }
    private void StopShooting()
    {
        _audioSource.Stop();
        _particle.Stop();
    }
    private void Shoot()
    {
        _timer = 0;
        Ray ray = _camera.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit = new RaycastHit();
        _audioSource.Play();
        _particle.Play();
        _currentAmmo--;
        _lineRenderer.SetPosition(0, GunEndPoint.position);
        StartCoroutine(FireLine());
        if (Physics.Raycast(ray, out hit, Range, _shootableMask))
        {
            print("hit " + hit.collider.gameObject);
            _lineRenderer.SetPosition(1, hit.point);
            EnemyHealth health = hit.collider.GetComponent<EnemyHealth>();
            EnemyMovement enemyMovement = hit.collider.GetComponent<EnemyMovement>();
            if (enemyMovement != null)
            {
                enemyMovement.KnockBack();
            }
            if (health != null)
            {
                health.TakeDamage(1);
            }
        }
        else
        {
            _lineRenderer.SetPosition(1, ray.GetPoint(Range));
        }
    }
    private IEnumerator FireLine()
    {
        _lineRenderer.enabled = true;
        yield return ShootingDelay - 0.05f;
        _lineRenderer.enabled = false;
    }
    // called from the animation finished
    public void ReloadFinish()
    {
        _isReloading = false;
        _currentAmmo = MaxAmmo;
    }
    private void SetupSound()
    {
        _audioSource = gameObject.AddComponent<AudioSource>();
        _audioSource.volume = 0.2f;
        _audioSource.clip = ShotSfxClips;
    }
}
 

The code flow for this is straightforward:

  1. We made 2 new variables MaxAmmo and _currentAmmo to represent how many bullets we have left to shoot.
  2. In Start() we initialize our current ammo amount.
  3. Whenever we shoot, we make sure that our ammo is above 0 otherwise we can’t shoot and then we decrement our _currentAmmo count.
  4. When we finish reloading, we’ll restore our ammo to max.

With the code, we have one problem, while we’re shooting, if we never let go of our mouse, we’ll continue to play the shooting animation and sound effect. We need to change this.

I fixed the problem by adding another check to make sure that we stop shooting when we either let go of our mouse or when we run out of bullets.

Here’s the change we did for Update():

   
  void Update ()
    {
        _timer += Time.deltaTime;
        // Create a vector at the center of our camera's viewport
        Vector3 lineOrigin = _camera.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 0.0f));
        // Draw a line in the Scene View  from the point lineOrigin in the direction of fpsCam.transform.forward * weaponRange, using the color green
        Debug.DrawRay(lineOrigin, _camera.transform.forward * Range, Color.green);
        if (Input.GetMouseButton(0) && _timer >= ShootingDelay && !_isReloading && _currentAmmo > 0)
        {
            Shoot();
            if (!_isShooting)
            {
                TriggerShootingAnimation();
            }
        }
        else if (!Input.GetMouseButton(0) || _currentAmmo <= 0)
        {
            StopShooting();
            if (_isShooting)
            {
                TriggerShootingAnimation();
            }
        }
        if (Input.GetKeyDown(KeyCode.R))
        {
            StartReloading();
        }
    }
 

Now that we have the script for reloading and shooting, we need to add our shooting mechanisms.

The first thing we’re going to do is to go to our original HUD component that we created in Day 10 and create a new script called ScreenManager.

The script will be used for ammo and later score count, and in the future health. Here’s our code:

 
using UnityEngine;
using UnityEngine.UI;
public class ScreenManager : MonoBehaviour
{
    public Text AmmoText;
    void Start()
    {
        {
            PlayerShootingController shootingController = Camera.main.GetComponentInChildren<PlayerShootingController>();
            UpdateAmmoText(shootingController.MaxAmmo, shootingController.MaxAmmo);
        }
    }
    public void UpdateAmmoText(float currentAmmo, float maxAmmo)
    {
        AmmoText.text = currentAmmo + "/" + maxAmmo;
    }
}
 

Here’s how our code works:

  1. We take in the Text game object that we use for ammo.
  2. In Start() we initialize our text ammo by getting our PlayerShootingController that’s a child of our camera and using the max ammo value we set
  3. Inside UpdateAmmoText() we give it the ammo amount to print out. I made this public, because we want to call this function from elsewhere.

There was a design decision that I was thinking of as I was going through this.

I was looking back at the score UI that was made for the Survival Shooter tutorial and it used static variables to represent the score. Static meaning you can access is anywhere, anytime without needing access to the script itself.

That worked in the context that only the manager needed to know anything about the score, however in our case, we already have our ammo amount in our PlayerShootingController, so if that’s the case, there’s no need to keep a separate instance to keep track of our ammo amount.

Instead let’s just pass in the values that we want the text to print out.

Another benefit of this is that we don’t have to re-render our text every single time we call Update() and we only change it when we need to.

The only downside is that we must get an instance of ScreenManagerwhenever we want to make any changes as opposed to the static method that was used in the tutorial.

Updating our PlayerShootingController, here’s what we get:

 
using UnityEngine;
using System.Collections;
public class PlayerShootingController : MonoBehaviour
{
    public float Range = 100;
    public float ShootingDelay = 0.1f;
    public AudioClip ShotSfxClips;
    public Transform GunEndPoint;
    public float MaxAmmo = 10f;
    private Camera _camera;
    private ParticleSystem _particle;
    private LayerMask _shootableMask;
    private float _timer;
    private AudioSource _audioSource;
    private Animator _animator;
    private bool _isShooting;
    private bool _isReloading;
    private LineRenderer _lineRenderer;
    private float _currentAmmo;
    private ScreenManager _screenManager;

    void Start () {
        _camera = Camera.main;
        _particle = GetComponentInChildren<ParticleSystem>();
        Cursor.lockState = CursorLockMode.Locked;
        _shootableMask = LayerMask.GetMask("Shootable");
        _timer = 0;
        SetupSound();
        _animator = GetComponent<Animator>();
        _isShooting = false;
        _isReloading = false;
        _lineRenderer = GetComponent<LineRenderer>();
        _currentAmmo = MaxAmmo;
        _screenManager = GameObject.FindWithTag("ScreenManager").GetComponent<ScreenManager>();
    }
    
    void Update ()
    {
        _timer += Time.deltaTime;
        // Create a vector at the center of our camera's viewport
        Vector3 lineOrigin = _camera.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 0.0f));
        // Draw a line in the Scene View  from the point lineOrigin in the direction of fpsCam.transform.forward * weaponRange, using the color green
        Debug.DrawRay(lineOrigin, _camera.transform.forward * Range, Color.green);
        if (Input.GetMouseButton(0) && _timer >= ShootingDelay && !_isReloading && _currentAmmo > 0)
        {
            Shoot();
            if (!_isShooting)
            {
                TriggerShootingAnimation();
            }
        }
        else if (!Input.GetMouseButton(0) || _currentAmmo <= 0)
        {
            StopShooting();
            if (_isShooting)
            {
                TriggerShootingAnimation();
            }
        }
        if (Input.GetKeyDown(KeyCode.R))
        {
            StartReloading();
        }
    }
    private void StartReloading()
    {
        _animator.SetTrigger("DoReload");
        StopShooting();
        _isShooting = false;
        _isReloading = true;
    }
    private void TriggerShootingAnimation()
    {
        _isShooting = !_isShooting;
        _animator.SetTrigger("Shoot");
    }
    private void StopShooting()
    {
        _audioSource.Stop();
        _particle.Stop();
    }
    private void Shoot()
    {
        _timer = 0;
        Ray ray = _camera.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit = new RaycastHit();
        _audioSource.Play();
        _particle.Play();
        _currentAmmo--;
        _screenManager.UpdateAmmoText(_currentAmmo, MaxAmmo);
        _lineRenderer.SetPosition(0, GunEndPoint.position);
        StartCoroutine(FireLine());
        if (Physics.Raycast(ray, out hit, Range, _shootableMask))
        {
            print("hit " + hit.collider.gameObject);
            _lineRenderer.SetPosition(1, hit.point);
            EnemyHealth health = hit.collider.GetComponent<EnemyHealth>();
            EnemyMovement enemyMovement = hit.collider.GetComponent<EnemyMovement>();
            if (enemyMovement != null)
            {
                enemyMovement.KnockBack();
            }
            if (health != null)
            {
                health.TakeDamage(1);
            }
        }
        else
        {
            _lineRenderer.SetPosition(1, ray.GetPoint(Range));
        }
    }
    private IEnumerator FireLine()
    {
        _lineRenderer.enabled = true;
        yield return ShootingDelay - 0.05f;
        _lineRenderer.enabled = false;
    }
    // called from the animation finished
    public void ReloadFinish()
    {
        _isReloading = false;
        _currentAmmo = MaxAmmo;
        _screenManager.UpdateAmmoText(_currentAmmo, MaxAmmo);
    }
    private void SetupSound()
    {
        _audioSource = gameObject.AddComponent<AudioSource>();
        _audioSource.volume = 0.2f;
        _audioSource.clip = ShotSfxClips;
    }
}
 

Here’s what we did:

  1. We looked for our ScreenManager by looking for it via a tag we set on it called ScreenManager.
  2. Any time we change our ammo amount we would call UpdateAmmoText() in our _screenManager. In this case, there are 2 places: after we shoot and after we reload.

Now before we try our game, first go to our HUD game object that we attached our ScreenManager script to, and create a new Tag called ScreenManager and make that the Tag for HUD.

If we were to play the game now, you’ll see that when we shoot, our ammo goes down, and when we hit R to reload, it goes back up to our maximum amount!

 
0*51w3MiBsIBfFqdSk.png

Conclusion

That’s it for today! I thought I would get to do more, but it looks like that was not the case for me!

To recap everything we did today, we created an ammo system where after we shoot all of our bullets we have to reload.

Afterwards we create a new Text that represent our ammo count on the bottom right hand corner of the screen.

I think tomorrow on Day 19, we’ll continue to add more pieces of the game like player health and a scoring system.

Source: Day 18

Visit the 100 Days of Unity VR Development main page.

Visit our Homepage



0 Comments


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
  • Advertisement
  • Blog Entries

  • Similar Content

    • By JustACicada
      Random Number God has been updated to v1.1.0.
      This is an incremental (although not idle) game about defeating randomized robots by rolling dice and playing cards that alter those dice and their effects.
      Other than performance fixes, the game has been rebalanced from the ground up. Now it should progress in a more fluid fashion. An option to reset the game with a significant boost to your power has been added, allowing you to advance further than you could before.
      There is also now an option to significantly speed up battle animations. Once you learn the rules of the game, a battle can easily take <2 min.
      Windows, Linux: https://justacicada.itch.io/random-number-god
      Android: https://play.google.com/store/apps/details?id=samuelVazquez.randomNumberGod


    • By Jamesgz
      Hey my dudes,
      Me and 4 friends are third year compsci students. Three of us are pretty good at drawing. We are hoping to make a 2d roguelite game with unity during the next few months. We are still brainstorming. At the moment, my idea is to create a card roguelite game:
      First, you would need to choose 2 heroes to enter the dungeon with the goal of finding a treasure. The treasure found gives you extra bonus in later runs. You can choose between mage, gunner, rogue, paladin, warrior and fighter. Each hero has their own unique cards. And there are common cards that every heroes can get(like hearthstone).
      The progression system would be like slay the spire’s. You can choose your own path, but every paths leads to the boss. It would use procedural generation. After defeating an enemy, you get to choose a new card out of the three options. There would be shops, random events, elite enemies, etc
      The combat system is where i need some suggestions on. There would be two piles of deck. One for each hero. I can think of two good combat systems:
      1. Before every enemy encounters, you can choose what cards to use from your deck. Cards not used would not get discarded. Cards are drawn from the deck only if they break or due to special card’s effect. Every card have a durability number. Ones the durability reach zero, the card would break and can no longer be used. Events/enemies can modify the durability of the cards.
      2. Card not used this turn would get discarded. Once the deck is empty, the discard pile gets shuffled and copied to the deck. Card/item effects can increase the number of cards you draw.
      How can I make the game more interesting? Any suggestions would be appreciated.
    • By horror_man
      Hello, I'm currently searching for a talented and passionate programmer to create a small but great horror game that would take around 3 months to be done.
       
      About the game: The game would be a sci-fi/post-apocalyptic survival horror 3D game with FPS (First person shooter) mechanics and an original setting and story based in a book (which I'm writing) scene, where a group of prisoners are left behind in an abandoned underground facility. It would play similar to Dead Space combined with Penumbra and SCP: Secret Laboratory, with the option of playing solo or multiplayer.
       
      Engine that'd be used to create the game: Unity
       
      About me: I'm a music composer with 4 years of experience and I'm fairly new in this game development world, and I'm currently leading the team that'd be creating this beautiful and horrifying game. I decided that making the book which I'm writing into a game would be really cool, and I got more motivated about doing so some time ago when I got a bunch of expensive Unity assets for a very low price. However, I researched about how to do things right in game development so I reduced the scope of it as much as I could so that's why this game is really based in a scene of the book and not the entire thing (and also that's why it would take 3 months). Also I'm currently learning how to use Unity and how to model things with Blender.
       
      Our team right now consists of: Me (Game Designer, Creator, Music Composer, Writer), 3 3D Modelers, 1 Sound Effect Designer, 1 Concept Artist and 1 Programmer.
       
      Who am I looking for:
      - A programmer that's experienced in C# and with Unity.
       
      Right now the game is very early in its development (GDD is completed and all 3D Items, Music and Sound Effects are completed).
       
      If you are interested in joining, contributing or have questions about the project then let's talk. You can message me in Discord: world_creator#9524
    • By kcirkl
      Hello, 
      So, I'm aware this may be a broad question with a vast variety in the anwser. However, I am a game design student that works professionally for a large engineering company conducting software configuration. Naturally, I'm curious about the path ahead and I'm desperately gathering information on the overall process of developement with Unity. Are there professional resources available that can outline the processes and functions of the engine, and what can be used to interact with it? I'm looking for options other than the official documentation. I have already jumped into that. Thanks!
    • By tspartant
      Hey everyone! My name is Ryan and I am the founder of Visualistic Studios, LLC. 
      I'm looking for experienced developers interested in short term and long term contracting. For the past 3 years I've been working in game development contracting, and the past year I've been working full time from home. Since then, I've received more and more contracts and I'm now at the point that I have too many for myself to handle. I have at least another full time job's worth of programming offers, and around 30-80 hours a month for 3D modeling/animation. I also am in need of a UI artist, so if you're talented please contact me! I have contracts using Unity as well as Unreal Engine 4, so if you can program in either please contact me. 
      If you are interested in working on these contracts, please send me links to your work and you hourly rate. (Most contracts range be between 18-25$/h, but please provide your normal hourly rate)
      You can get ahold of me through email - "ryan.hobbs@visualisticstudios.com", or Discord "TSpartanT#4670"
       
      Thank you everyone for reading, hope to hear from you soon!
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!