Posts tagged ‘Unity’

Dream Build Play 2017 Dev Log #1

Yes, I’m late in getting started with this, but better late than never I guess. :\

I’m attempting to get a game that I’ve had on the back burner for a couple years completed for Microsoft’s new Dream Build Play competition using Unity and the relatively new Xbox Live Creators Program. Over the past week or so I’ve been looking into the Xbox Live functionality offered when using Unity and the results have been less than great.

It seems the documentation for integrating the functionality into Unity is not complete in the steps required. I posted the problem on the Unity Indie Dev Facebook page and after some back and forth with the awesome Simon Jackson we seem to have come up with a solution, which he recently released a blog post about.

The game I’m working on is one I’ve been kicking around for years, an online and local bot racing/battle game. I’m hoping to include some features that make it attractive – an editor for custom levels that can be shared between all players for infinite replayability, upgrading bots by using earnings from winning games, “awardments” (since XLCP games can’t have Xbox achievements), and leaderboards. If nothing else, I want to at least finally get a game out there. I’ve spent so many years helping others, writing books, and putting out samples that I’ve yet to get a game on a store as an indie game dev. 🙁 While all the things I’ve done have been rewarding it’s a goal I want to cross off my list.

The main work so far besides trying to get the Xbox Live functionality working has been setting up the skeleton of the game – menus & scenes, main game objects, etc. I do this with all my games. I really should make a package out of it to make things easier every time I start a new game, but I never seem to get around to it. It only takes about a half-hour each time, but with all the games I start I’ve wasted at least a dozen hours or two. 🙁 Here’s my typical folder structure:

Since this was a game that I’d started developing in XNA, I already had some existing content. The “Levels” and “Sprites” content were brought in from that game. There will be the standard levels that ship with the game and custom levels that players develop and will hopefully be able to share with other players. Those I’m hoping to store in the cloud using Azure (to score some bonus points in the DBP contest ;)).

Even before creating all this, I set about trying to get the Xbox Live login functionality to work. I’d tried getting it to work in a skeleton project and run into problems so I started a new project with a single scene and tried again. It’s supposedly as easy as dropping a couple of prefabs into a scene and it should just all magically work. That doesn’t seem to be the case however. The prefab tries to automatically sign me in, but fails:

After going through possibilities for a week, I stumbled across a step that I’d missed. This page talks about getting a new title set up in the Xbox Live system. The step where you push your game’s configuration to the test environment I read as something that you did if you’re testing an Xbox version of a game. Since what I’m working on is for the PC, for now, I’d skipped over it. Turns out it needs to be done for any platform using the Xbox Live functionality. :\ I did that and, viola:

So now on to figuring out Leaderboards! 🙂

So Many Stars

While many game services and platforms have built-in ratings system, there may come a time when you, like me currently, need a way to allow a user to rate your game using a standard 5 star system. I took a bit of time today to put together a scene to handle this.

First I created filled and outline star graphics:

 

Next I created a scene and laid out a couple of buttons and the 5 star outlines:

The stars are Image UI components. The OK button is not interactable until a rating is selected.

Next I added some event triggers to the stars:

The only unusual thing about this is I added a parameter to the standard event handler:

public void StarEnter(GameObject obj)

public void StarExit(GameObject obj)

public void StarClick(GameObject obj)

Since I’m using the same 3 events for all 5 stars I needed a way to tell which star is being handled. I didn’t want to have to go through some convoluted chunk of code to figure out which star the mouse was over. This would have involved doing a raycast from the mouse location and seeing it the ray hit something, then figuring out which star it hit. Once I set up the empty events in my script the Unity UI automatically recognized the need for a GameObject parameter.

Here’s the entire Rating class:

using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using UnityEngine.EventSystems;
using System;
using System.Collections.Generic;

 

public class Rating : MonoBehaviour {

 

    public Image Star1;
    public Image Star2;
    public Image Star3;
    public Image Star4;
    public Image Star5;

 

    public Sprite Star;
    public Sprite StarOutline;

 

    private int _ratingSelected = 0;

 

    public Button OKButton;

 

    void Start()
    {
        //check for existing rating and disable if exists
        if (PlayerPrefs.HasKey("Rating"))
        {
            StarEnter(GameObject.Find(PlayerPrefs.GetInt("Rating").ToString() + "Star"));

 

            _ratingSelected = PlayerPrefs.GetInt("Rating");
        }
    }

 

    public void StarEnter(GameObject obj)
    {
        if (_ratingSelected == 0)
        {
            switch (obj.name)
            {
                case "1Star":
                {
                    Star1.sprite = Star;

 

                    break;
                }
                case "2Star":
                {
                    Star1.sprite = Star;
                    Star2.sprite = Star;

 

                    break;
                }
                case "3Star":
                {
                    Star1.sprite = Star;
                    Star2.sprite = Star;
                    Star3.sprite = Star;

 

                    break;
                }
                case "4Star":
                {
                    Star1.sprite = Star;
                    Star2.sprite = Star;
                    Star3.sprite = Star;
                    Star4.sprite = Star;

 

                    break;
                }
                case "5Star":
                {
                    Star1.sprite = Star;
                    Star2.sprite = Star;
                    Star3.sprite = Star;
                    Star4.sprite = Star;
                    Star5.sprite = Star;

 

                    break;
                }
            }
        }

 

    }

 

    public void StarExit(GameObject obj)
    {
        if (_ratingSelected == 0)
        {
            switch (obj.name)
            {
                case "1Star":
                {
                    Star1.sprite = StarOutline;

 

                    break;
                }
                case "2Star":
                {
                    Star1.sprite = StarOutline;
                    Star2.sprite = StarOutline;

 

                    break;
                }
                case "3Star":
                {
                    Star1.sprite = StarOutline;
                    Star2.sprite = StarOutline;
                    Star3.sprite = StarOutline;

 

                    break;
                }
                case "4Star":
                {
                    Star1.sprite = StarOutline;
                    Star2.sprite = StarOutline;
                    Star3.sprite = StarOutline;
                    Star4.sprite = StarOutline;

 

                    break;
                }
                case "5Star":
                {
                    Star1.sprite = StarOutline;
                    Star2.sprite = StarOutline;
                    Star3.sprite = StarOutline;
                    Star4.sprite = StarOutline;
                    Star5.sprite = StarOutline;

 

                    break;
                }
            }
        }
    }

 


    public void StarClick(GameObject obj)
    {
        _ratingSelected = Convert.ToInt32(obj.name.Substring(0, 1));
        OKButton.interactable = true;
    }

 

    public void OKButton_Click()
    {
        //save rating
        PlayerPrefs.SetInt("Rating", _ratingSelected);
        Application.LoadLevel("Menu");
    }

 

    public void CancelButton_Click()
    {
        Application.LoadLevel("Menu");
    }
}
}

Since we don’t want the player to be able to change a rating once it’s made, the first thing we do is check to see if the player has already rated the game. I’m storing this in the PlayerPrefs for convenience sake. Obviously it should be in a place a little more secure. If the player has already rated the game, we call the StarEnter handler, passing the control for that number of stars, which sets the related number of stars. We then set the _ratingSelected member with the value from the PlayerPrefs key. Since the event handlers don’t do anything if the _ratingSelected member has a value other than 0, this effectively disables them.

The StarExit handler resets the images to the star outline. We could have simply not passed a parameter to it and just reset all of the stars, but I like the consistency of having a parameter for all 3 handlers.

If the player clicks a star, the _ratingSelected member is set and the OK button enabled, allowing the player to save the rating.

Pretty straightforward. If you use this as is, you’ll have to have a scene named “Menu” and Image controls with the names I used or you’ll get an error.

Feel free to comment – is this useful? Would you have done something different? Am I wasting my time here? 🙂

Creating a Space Shooter Weapon System

So a question came up the other day from Dave Voyles about how to design a weapon system for a space shooter. He needed to figure out how to track what weapons a player had collected and how to switch between them. I got to thinking about it and decided to see what I could come up with. Here’s the basic requirements that I figured would be good to have:

  • A ship could start out with no weapons or 1 or more weapons, with or without ammo
  • A weapon can have infinite ammo or require ammo pickups
  • The player shouldn’t be able to switch to a weapon that hasn’t been equipped on the ship or if it doesn’t have ammo
  • An ammo type could be shared between weapons (think universal energy cells for energy powered weapons)
  • Ammo pickups could have different amounts of ammo for an ammo type
  • Switching between weapons could wrap around both forward or backward. For example, using an Xbox controller, the left and right bumpers could be used to switch to the previous or next weapon – if the first weapon is selected and the left bumper is pressed, the last weapon that’s equipped would be selected.
  • An icon on the game’s HUD would show the currently selected weapon and the ammo available for that weapon would be displayed.

Starting from an empty Unity project, we’ll only need one scene and some GUI elements in that scene. I’m using buttons for all of the player input in this sample, but you would want to handle whatever method of input you’re targeting for your game, touch included. Here’s what my example looks like:

The 5 images on the right are used to display the currently selected weapon, which is the blank image between the Next and Previous buttons. Notice that the button to fire the weapon is initially set to disabled. Whether or not to enable this will be handled by the script we’ll attach to the scene. The small images above the currently selected weapon image will show the weapons that have been equipped on the ship. The buttons to pickup weapons and ammo would be replaced in a real game with sprites or some other element that the player’s ship would interact with.

The next thing we’ll need is a script to control everything and a script to define weapons and ammo. We’ll look at the latter first:

using UnityEngine.UI;

public enum AmmoType
{
    Regular,
    Shotgun,
    Sniper,
    Energy
}

public enum WeaponType
{
    Vulcan,
    SoloGun,
    Sniper,
    ShotGun,
    Seeker
}

public class Weapon
{
    public WeaponType Type;
    public Image HUDIcon;
    public AmmoType Ammo;
    public int AmmoCount;
}

 

For our basic example this is all we’ll need. More than likely your weapon class would have more members and functionality and you might even have an ammo class.

Notice that we only have 4 ammo types. The SoloGun and the Seeker I decided were energy weapons and will share whatever energy ammo is available.

The script to control the interaction with the weapons is only a bit more complex in my opinion:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using System;
using UnityEngine.EventSystems;

public class WeaponInventory : MonoBehaviour
{
    private int[] weaponAmmoRate = new int[] { 10, 1, 1, 1, 5 };

    Dictionary<WeaponType, Weapon> weapons;
    WeaponType curWeaponIndex = WeaponType.Vulcan;

    public Image CurWeaponIcon;

    public Image VulcanImage;
    public Image SoloGunImage;
    public Image SniperImage;
    public Image ShotgunImage;
    public Image SeekerImage;


    public Image MiniVulcanIcon;
    public Image MiniSoloGunIcon;
    public Image MiniSniperIcon;
    public Image MiniShotgunIcon;
    public Image MiniSeekerIcon;


    public Button FireButton;

    public Text AmmoDisplay;

    public bool WrapWeapons = true;

    private Dictionary<AmmoType, int> Ammo; //-1 for infinite


	// Use this for initialization
	void Start () {

        weapons = new Dictionary<WeaponType, Weapon>();

        weapons.Add(WeaponType.Vulcan, new Weapon() { Type = WeaponType.Vulcan, HUDIcon = VulcanImage, Ammo= AmmoType.Regular });

        MiniVulcanIcon.color = new Color(1.0f, 1.0f, 1.0f, 1.0f);

        Ammo = new Dictionary<AmmoType, int>();

        Ammo.Add(AmmoType.Regular, 100);
        Ammo.Add(AmmoType.Energy, 0);
        Ammo.Add(AmmoType.Shotgun, 0);
        Ammo.Add(AmmoType.Sniper, 0);

        FireButton.interactable = true;

        GetCurWeaponImage();
	}

    public void NextWeapon()
    {
        if (!WrapWeapons && (int)curWeaponIndex == Enum.GetValues(typeof(WeaponType)).GetUpperBound(0))
            return;
        else
        {
            WeaponType originalIndex = curWeaponIndex;

            curWeaponIndex++;
            while (!weapons.ContainsKey(curWeaponIndex))
            {
                if ((int)curWeaponIndex > Enum.GetValues(typeof(WeaponType)).GetUpperBound(0))
                    curWeaponIndex = 0;
                else
                { 
                    curWeaponIndex++;
                    if (curWeaponIndex == originalIndex)
                        break;
                }
            }

            GetCurWeaponImage();
            CheckWeapon();
        }
    }

    public void PreviousWeapon()
    {
        if (!WrapWeapons && (int)curWeaponIndex == 0)
            return;
        else
        {
            WeaponType originalIndex = curWeaponIndex;

            curWeaponIndex--;
            while (!weapons.ContainsKey(curWeaponIndex))
            {
                if ((int)curWeaponIndex < 0)
                    curWeaponIndex = (WeaponType)Enum.GetValues(typeof(WeaponType)).GetUpperBound(0);
                else
                {
                    curWeaponIndex--;
                    if (curWeaponIndex == originalIndex)
                        break;
                }
            }

            GetCurWeaponImage();
            CheckWeapon();
        }
    }

    private void CheckWeapon()
    {
        bool enable = Ammo[weapons[curWeaponIndex].Ammo] > 0;

        FireButton.interactable = enable;
    }

    private void GetCurWeaponImage()
    {
        CurWeaponIcon.sprite = weapons[curWeaponIndex].HUDIcon.sprite;

        UpdateAmmoDisplay();
    }

    public void PickupVulcanWeaponButtonClick()
    {
        if (!weapons.ContainsKey(WeaponType.Vulcan))
        { 
            weapons.Add(WeaponType.Vulcan, new Weapon() { Type = WeaponType.Vulcan, HUDIcon = VulcanImage, Ammo = AmmoType.Regular });
            MiniVulcanIcon.color = new Color(1.0f, 1.0f, 1.0f, 1.0f);
        }
    }

    public void PickupSoloGunWeaponButtonClick()
    {
        if (!weapons.ContainsKey(WeaponType.SoloGun))
        {
            weapons.Add(WeaponType.SoloGun, new Weapon() { Type = WeaponType.SoloGun, HUDIcon = SoloGunImage, Ammo = AmmoType.Energy });
            MiniSoloGunIcon.color = new Color(1.0f, 1.0f, 1.0f, 1.0f);
        }
    }

    public void PickupSniperWeaponButtonClick()
    {
        if (!weapons.ContainsKey(WeaponType.Sniper))
        { 
            weapons.Add(WeaponType.Sniper, new Weapon() { Type = WeaponType.Sniper, HUDIcon = SniperImage, Ammo = AmmoType.Sniper });
            MiniSniperIcon.color = new Color(1.0f, 1.0f, 1.0f, 1.0f);
        }
    }

    public void PickupShotgunWeaponButtonClick()
    {
        if (!weapons.ContainsKey(WeaponType.ShotGun))
        { 
            weapons.Add(WeaponType.ShotGun, new Weapon() { Type = WeaponType.ShotGun, HUDIcon = ShotgunImage, Ammo = AmmoType.Shotgun });
            MiniShotgunIcon.color = new Color(1.0f, 1.0f, 1.0f, 1.0f);
        }
    }

    public void PickupSeekerWeaponButtonClick()
    {
        if (!weapons.ContainsKey(WeaponType.Seeker))
        { 
            weapons.Add(WeaponType.Seeker, new Weapon() { Type = WeaponType.Seeker, HUDIcon = SeekerImage, Ammo = AmmoType.Energy });
            MiniSeekerIcon.color = new Color(1.0f, 1.0f, 1.0f, 1.0f);
        }
    }


    public void FireWeaponButtonClick()
    {
        Ammo[weapons[curWeaponIndex].Ammo] -= weaponAmmoRate[Convert.ToInt32(weapons[curWeaponIndex].Ammo)];

        if (Ammo[weapons[curWeaponIndex].Ammo] == 0)
            FireButton.interactable = false;

        UpdateAmmoDisplay();
    }

    private void UpdateAmmoDisplay()
    {
        AmmoDisplay.text = Ammo[weapons[curWeaponIndex].Ammo].ToString();
    }

    public void PickupRegularAmmoButtonClick()
    {
        Ammo[AmmoType.Regular] += Convert.ToInt32(EventSystem.current.currentSelectedGameObject.tag);
        CheckWeapon();
        UpdateAmmoDisplay();
    }

    public void PickupEnergyAmmoButtonClick()
    {
        Ammo[AmmoType.Energy] += Convert.ToInt32(EventSystem.current.currentSelectedGameObject.tag);
        CheckWeapon();
        UpdateAmmoDisplay();
    }

    public void PickupShotgunAmmoButtonClick()
    {
        Ammo[AmmoType.Shotgun] += Convert.ToInt32(EventSystem.current.currentSelectedGameObject.tag);
        CheckWeapon();
        UpdateAmmoDisplay();
    }

    public void PickupSniperAmmoButtonClick()
    {
        Ammo[AmmoType.Sniper] += Convert.ToInt32(EventSystem.current.currentSelectedGameObject.tag);
        CheckWeapon();
        UpdateAmmoDisplay();
    }

}

I had originally planned on using an enum for weaponAmmoRate, but the duplicate values made that impractical. Obviously the values need to match the order of the WeaponType enum.

The weapons variable holds all the possible weapons a ship can carry, even if they’re not equipped. If you add new weapons to the WeaponType enum, there will automatically be room for them in the possible weapons the ship can equip, no change necessary.

The curWeaponIndex is straightforward, it points to the item in the weapons array that’s currently selected.

CurWeaponIcon is the image in the scene that shows the player the weapon that’s currently selected. We need the member here to allow us to change it when the player uses an input device to switch between weapons. We could grab the Image every time the weapon is switched, but that’s cumbersome, more code than we need to use and wastes time. You’ll see this a lot in Unity, having a member in a script that is hooked to a UI object. This is one of the features of Unity that I really like. The Unity IDE recognizes the public members of a script and allows you to set them by dragging something from the Hierarchy onto it in the Inspector.

The next two chunks of Images are used to set the CurWeaponIcon and display which weapons the ship has equipped respectively.

We have a reference to the button that fires the selected weapon as we need to enable or disable it when the selected weapon is changed or if the selected weapon runs out of ammo.

As the player fires the selected weapon, we use the AmmoDisplay member to update the amount of ammo that’s available for the weapon.

Some games allow the selection of the weapon to wrap from last to first or vice versa when navigating through the equipped weapons. The WrapWeapons member allows us to decide whether or not we enable this feature. The code for handling the navigation between weapons uses this member to correctly set the selected weapon as we’ll see shortly.

The Ammo member holds the amount of ammo for every type of weapon. In the case of the some weapons you might want this is be infinite. This is usually the case with the default or first weapon that’s equipped in the ship. Setting the value to –1 for a weapon allows for infinite ammo for a weapon.

At the start of the scene we create our weapons array, add a Vulcan to it, set all the UI elements to indicate we have a weapon set, and add some ammo for it.

In the NextWeapon and PreviousWeapon methods we first check to see if the current weapon is the last or first respectively and if weapon wrap is not turned on we just return as there’s nothing to do. We then go through the weapons list checking to see if a weapon has been equipped on the ship to select. If we go through the entire list and don’t find one we just re-select the original weapon. We set the select weapon icon and check to see if the weapon has ammo, disabling the fire button if not. We also update the display of the available ammo for the selected weapon.

For each of the method for picking up a weapon we first see if the weapon is already equipped. If not, we create an instance in the weapons list and set the icon to indicate that weapon is available.

When the fire button is pressed we subtract from the ammo for that weapon based on the weapon rate for the weapon and disable the weapon is the ammo has run out. There’s a potential bug in this code. See if you can find it. 😉

When ammo is picked up, we grab the Tag property of the GameObject to see how much ammo it contains and add that to the proper element in the Ammo array. One thing that you could change for a real game is add a property to the ammo GameObject to indicate what type of ammo it is and have one method for handling all ammo pickups. Try replacing the buttons with a GameObject that does this.

That’s it. Pretty simple I think, but it should be enough to at least start you out with a space shooter type game. As also, feedback and questions are welcome. 🙂