After writing the last tutorial on Inventory systems I discovered this helped me think when trying out new stuff! So here is another that I hope helps someone.

So here is a small tutorial on making your own Shortcut bar using ORK systems. Essentially we are "actually* using ORKS shortcut bar but just not drawing it, instead drawing our own.

But... Why?
Fair question, because I want to is a fair answer too, but this custom GUI allows you to do a bit more than the default ORK shortcut system, as functional as it is, may allow.

I've added just one basic feature, a cool down timer based off the Reuse Time in ORK. The button number 2 is based off a mana effect and when used, consumes mana and greys out if there is not enough mana to reuse, then becomes enabled later when there IS mana avaliable!

image

Other things you could add:
- Animate the shortcuts onto and off the screen in various ways/times (on use, button "falls" off the screen!)
- Particle effect "sparkle" firing when a shortcut because usable again
- FX when a new shortcut item gets added to the bar (during a tutorial maybe? New skills learned?
- A rhythm game where shortcuts are more powerful depending on the music beat in the background (FX when they are powered up)
- Combo skills, FX when a third ability shortcut because available after a combo of various other abilities
- Animate the "item" on a skillslot floating up off it and the item counter slowing a -1 down on use
- Skillbar slots placed on objects in the game world itself (so you need to rotate and click on whatever you are battling "Head shot" skill literally on the back of monsters head....)

To be honest, ORK can probably do a lot of this already, but I had tools and skills on standard GUI code and it was trivial to make this so why not!

Systems
We are going to use 1 prefab (for each button on the skillbar) and 1 prefab for the bar itself

Each skill slot prefab consists of a fair few game objects:
- A button for the pushing!
- Text to show the key bind for the shortcut
- Image for the skill icon (pulled from ORK)
- Quantity background image (So the text is visible over the image)
- Quantity text
- Cooldown timer image
- Cooldown text (to display the reuse time left to go)

You can add more obviously for extra effects as mentioned above. Particle Systems, etc.

This button will have a script attached to it which handles everything. This is then prefabbed and you can add as many buttons to your shortcut bar as you need!

Making the Shortcut bar in ORK!
First we need a shortcut bar in ORK Framework to handle all the hard stuff.

There is a tutorial here that shows how to create them.

Also ensure your player character HAS some abilities to use assigned to them and assigned to the bar.

When testing you can leave the ORK bar visible, and you should pretty much see the same in your bar and the ORK one.

When you are happy yours works, you can mark the ORK bar invisible by checking the box "No Display" in ORK Editor | Menus | HUDs, find your shortcut bar in the list and tick "No Display"

Making the Shortcut button prefab

So we are going to try and create the following and then prefab it:
image

1) Start out by creating a button (I suggest GUI | Button - TextMeshPro if you have that installed, else a standard button)
This gives you your button and a text gameobject. I sized my button to 48x48 and assigned a button source image of a nice blue empty button image
Rename the button something like "Button - Hotkey"
Rename the text to "KeybindLabel" - Move the text to above the button (Floating in space above it) and change the color to a sort of yellow.

2) Add a UI | Image as a child of the button, rename to "SkillIcon". I resized the image to 36x36

3) Add a UI | Image as another child of the button, rename to "QuantityBackground" - Resize this so it takes up the bottom right of the button (I resized mine to 15x15). Set the color to a black.

4) Add a UI | Text - TextMeshPro as a child of the "QuantityBackground" and rename to "QuantityLabel". Change font size to 14 and move so it sits nicely in the middle of the QuanityBackground. Set the text color to a light white or yellow.

5) Add a UI | Image as yet another child of the "Button - Hotkey" and rename to "TimerImage". This will be our fill timer for cooldowns. Resize to a size that covers the SkillIcon (36x36 in my case). Add a image to the Source Image that has an Alpha channel. A plain while square would work fine.
Change Image Type to "Radial"
Set "Fill Origin" to top and deselect Clockwise (Though you can play around with these last two to see their effects.)
Also play with "Fill Amount" so see how the final effect will work.
Change the color to something offwhite and 50% alpha. Again, play with these settings a bit for your own taste.
image

6) Lastly, add another text box as a child of "TimerImage" and call it "TimerText". Place it in the middle of the main window, decent font size so you can see it and keep the color off-white for the moment.

In the end you should have list of gameobjects like this:
image

Create a prefab of this and we will continue working on that.

Scripting the button
Now we need to add a script to the button itself. Create a script called "HotkeyButtonControl" on the Button gameobject.

Paste in the following code:


using ORKFramework;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using ORKEnums;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class HotkeyButtonControl : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler
{
[Tooltip("Text field showing the key bound to this hotkey/shortcut")]
[SerializeField]
private TextMeshProUGUI buttonInfoText;

[Tooltip("Text field to show the quantity of the shortcut (if any)")]
[SerializeField]
private TextMeshProUGUI quantityText;

[Tooltip("The ID for the shortcut key (1-9..0)")]
[SerializeField]
private int id;

[Tooltip("Image of the hotkey (Ability, item etc)")]
[SerializeField]
private Image hotkeyImage;

[Tooltip("Quanity value background image")]
[SerializeField]
private Image quanityBackgroundImage;

[Tooltip("Image user for ability timer function")]
[SerializeField]
private Image cooldownImage;

[Tooltip("Text field showing the time left (in seconds) until this ability can be used again")]
[SerializeField]
private TextMeshProUGUI cooldownText;

[Tooltip("Show the cooldown text field?")]
[SerializeField]
private bool showCooldownText;

protected Combatant playerCombatant = null;

private IShortcut currentShortcut = null;

private bool canUseState = true;
private int lastQuantityValue = 0;

private float currentMaxReuseTime = 0f;

public void Start()
{
StartCoroutine(GetPlayer());

// Initially clear the icons
hotkeyImage.enabled = false;
currentShortcut = null;
buttonInfoText.text = id.ToString(); // TODO: this needs to be changed to the real SHORTCUT key
quantityText.alpha = 0;

cooldownText.text = "";
cooldownText.alpha = 0;
cooldownImage.fillAmount = 0;
}

private IEnumerator GetPlayer()
{
do
{
playerCombatant = ORK.Game.ActiveGroup.Leader;
yield return null;
}
while (this.playerCombatant == null);

// ** Initialization code that requires a combatant here **
buttonInfoText.text = id.ToString(); // TODO: this needs to be changed to the real SHORTCUT key
}

// Update is called once per frame
public void Update()
{
if (playerCombatant != null)
{
IShortcut shortcut = playerCombatant.Shortcuts.Current[id];
if (shortcut != null)
{
if (currentShortcut == null || currentShortcut.ID != shortcut.ID || ((currentShortcut is ItemShortcut) && currentShortcut.Quantity != lastQuantityValue))
{
// we have a new shortcut lets deal with it
currentShortcut = shortcut;
cooldownText.text = "";
hotkeyImage.enabled = true;

if (currentShortcut is AbilityShortcut)
{
AbilityShortcut abs = (currentShortcut as AbilityShortcut);

Texture tex = abs.GetIcon();
hotkeyImage.sprite = Sprite.Create(tex as Texture2D, new Rect(0, 0, tex.width, tex.height), new Vector2(.5f, .5f));
quantityText.alpha = 0;
quanityBackgroundImage.color = new Color(quanityBackgroundImage.color.r, quanityBackgroundImage.color.g, quanityBackgroundImage.color.b, 0);
}
else if (currentShortcut is ItemShortcut)
{
Texture tex = (currentShortcut as ItemShortcut).GetIcon();
hotkeyImage.sprite = Sprite.Create(tex as Texture2D, new Rect(0, 0, tex.width, tex.height), new Vector2(.5f, .5f));
//; and display the QUANTIY count
lastQuantityValue = currentShortcut.Quantity;
quantityText.text = lastQuantityValue.ToString();
quantityText.alpha = 1;
quanityBackgroundImage.color = new Color(quanityBackgroundImage.color.r, quanityBackgroundImage.color.g, quanityBackgroundImage.color.b, 1);
}
}

bool canWeCurrentlyUseShortcut = currentShortcut.CanUse(playerCombatant, true, true);
if (canWeCurrentlyUseShortcut && !canUseState)
{
// enable button
canUseState = canWeCurrentlyUseShortcut;
hotkeyImage.color = new Color(1f, 1f, 1f, 1f);
}
else if (!canWeCurrentlyUseShortcut && canUseState)
{
canUseState = canWeCurrentlyUseShortcut;
hotkeyImage.color = new Color(0.4f, 0.4f, 0.4f, 0.4f);
}

// check to see if we need to update on screen cooldown timer
if (currentShortcut is AbilityShortcut)
{
AbilityShortcut abs = (currentShortcut as AbilityShortcut);
float abilityCooldownTime = abs.GetReuseTime(playerCombatant);
if (abilityCooldownTime > 0)
{
if (currentMaxReuseTime == 0)
{
currentMaxReuseTime = abilityCooldownTime;
cooldownImage.fillAmount = 1;
}

float percentVal = 1.0f / currentMaxReuseTime * abilityCooldownTime;
cooldownImage.fillAmount = percentVal;

if (showCooldownText)
{
cooldownText.alpha = 1;
string newText = abs.GetReuseTimeText(playerCombatant, 1); // Change the Decimal place here for WHOLE seconds (or whatever desired)
if (cooldownText.text != newText)
cooldownText.text = newText;
}
}
else
{
cooldownText.alpha = 0;
currentMaxReuseTime = 0f;
cooldownImage.fillAmount = 0;
}
}
}
else
{
// no shortcut, clear the icons
hotkeyImage.enabled = false;
currentShortcut = null;
quantityText.alpha = 0;
quanityBackgroundImage.color = new Color(quanityBackgroundImage.color.r, quanityBackgroundImage.color.g, quanityBackgroundImage.color.b, 0);
}
}
}

public void DoShortcutClick()
{
if (currentShortcut != null)
{
if (currentShortcut is AbilityShortcut)
{
if ((currentShortcut as AbilityShortcut).CanUse(playerCombatant, true, true))
{
(currentShortcut as AbilityShortcut).AutoUse(playerCombatant);
}
}
else if (currentShortcut is ItemShortcut)
{
if ((currentShortcut as ItemShortcut).CanUse(playerCombatant, true, true))
{
(currentShortcut as ItemShortcut).AutoUse(playerCombatant);
}
}
}
}

public void OnPointerEnter(PointerEventData eventData)
{
ORK.GUI.ForceCursorOver = true;
}

public void OnPointerExit(PointerEventData eventData)
{
ORK.GUI.ForceCursorOver = false;
}

public void OnPointerClick(PointerEventData eventData)
{
DoShortcutClick();
}
}


You'll notice the script has several properties you need to assign, they should be obviously named and there are tooltips. Your final script property list should look like the following:
image

Note the Id property, this is the Shortcut ID in ORK, so once you create a toolbar with your prefabs, you'll need to assign every different button a different ID (Pretty much 1 through to 9)

You can also disable the cooldown text entirely if you don't want to see it.

Now to the code, the points of interest are:

- OnPointerEnter and OnPointerExit are captured and ORK.GUI.ForceCursorOver set to ensure then while the mouse us OVER our button, clicks are not still sent down to the ORK GUI behind it.
- OnPointerClick is handled to receive button click events
- We use the ORK shortcut CanUse to determine if we CAN use the ability and if so, we just call AutoUse and let ORK deal with the while thing
- Currently as the code is designed we let ORK deal with the key press assigned to the shortcut in ORK Editor.
- The reuse timer takes the ORK reuse timer value and uses that to change the 0..1 value for the TimerImage fill amount to slowly uncover the image
- We directly output the ORK GetReuseTimeText to the TimerText. There are parameters in that call to change decimal places
- The code only updates the shortcut button icon IF the button has no shortcut and there should be one, or if the Shortcut ID has changed (Via a drag/drop event for example)
- Note I use the IEnumerator GetPlayer() stuff that was mentioned on these forums to ensure we don't try and make our buttons before a player has spawned. Useful code for all your ORK scripting

Shortcut bar
Once you have a shortcut button working and made into a prefab. just drag a few buttons onto a scene canvas, change the ID of every button to a different number matching a shortcut slot in ORK and you have your shortcut bar.

You can easily make a prefab of the whole bar for use over multiple scenes.

As for decoration I have done little here, just a single line on the bar behind the buttons.

Conclusion

There are likely bugs and situations not handled, hopefully this just gives you some ideas how to customise a ORK shortcut bar if the desire hits you.

We are missing the drag/drop interface ORK provides of course. We can make this easily enough, my other tutorial in the forums here has a full drag/drop base class that can give the Shortcut icons drag/drop capability very easily.

For this I'd add a new image in the shortcut bar prefab that has two states, locked and unlocked and based on that allow dragging of the shortcut bar. Something I'll work on next, I'll look at adding this to another post if anyone is interested.

Let me know if anyone has any questions!
  • Another great tutorial :)
    Please consider rating/reviewing my products on the Asset Store (hopefully positively), as that helps tremendously with getting found.
    If you're enjoying my products, updates and support, please consider supporting me on patreon.com!
  • Amazing!! Thank you for this!
  • edited December 2020
    Weird. In my last project it worked perfectly. In my current one, using ORKEnums doesn't work. Throws an error. EDIT: Never mind. I'm just an idiot. Still works amazingly!
    Post edited by Sierota1701 on
Sign In or Register to comment.